from datetime import date, datetime, timedelta
import uuid
import random
import logging
import math
import importlib
import json
from contextlib import contextmanager
from . import db
from .models import Attendantskills, ShiftAssignment, Elderdemand, DicNursingAction, ScheduleResult, TimeSlot
from config_params import shift_config, day_night_ratio, reserve_resource_ratio, params, shift_control, shift_mode_config, rest_time_control
from sqlalchemy import func

# 动态导入新算法模块
try:
    nsga2_module = importlib.import_module('app.nsga2')
    constraint_solver_module = importlib.import_module('app.constraint_solver')
    rl_scheduler_module = importlib.import_module('app.rl_scheduler')

    NSGA2Optimizer = nsga2_module.NSGA2Optimizer
    ConstraintSolver = constraint_solver_module.ConstraintSolver
    RLScheduler = rl_scheduler_module.RLScheduler

    ADVANCED_ALGORITHMS_AVAILABLE = True
except ImportError:
    ADVANCED_ALGORITHMS_AVAILABLE = False

class ThreeShiftScheduler:
    """三班制排班调度器"""

    def __init__(self):
        """初始化排班调度器"""
        # 基本配置
        self.shift_config = shift_config
        self.day_night_ratio = day_night_ratio
        self.reserve_resource_ratio = reserve_resource_ratio
        self.time_slots_per_day = params['time_slots_per_day']
        self.time_interval_minutes = params['time_interval_minutes']

        # 班制控制参数
        self.shift_control = shift_control
        self.shift_mode_config = shift_mode_config
        self.rest_time_control = rest_time_control

        # 初始化当前激活的班制模式
        self.active_mode = 'three_shift'  # 默认为三班制
        self.current_shift_times = {}

        # 复用原接口的算法参数
        self.population_size = params.get('population_size', 20)
        self.crossover_rate = params.get('crossover_rate', 0.3)
        self.mutation_rate = params.get('mutation_rate', 0.115)
        self.phenotype_limit = params.get('phenotype_limit', 200)
        self.elimination_size = params.get('elimination_size', 5)
        self.initial_temperature = params.get('initial_temperature', 100.0)
        self.cooling_rate = params.get('cooling_rate', 0.995)
        self.temperature = self.initial_temperature  # 当前温度，用于模拟退火

        # 初始化日志
        self.logger = logging.getLogger(__name__)
        if not self.logger.handlers:
            handler = logging.StreamHandler()
            formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
            handler.setFormatter(formatter)
            self.logger.addHandler(handler)
            self.logger.setLevel(logging.INFO)

        self.logger.info("初始化三班制排班调度器")
        self.logger.info(f"算法参数: population_size={self.population_size}, crossover_rate={self.crossover_rate}, "
                       f"mutation_rate={self.mutation_rate}, phenotype_limit={self.phenotype_limit}, "
                       f"initial_temperature={self.initial_temperature}, cooling_rate={self.cooling_rate}")

        # 计算班次时间
        self.calculate_shift_times()

        # 初始化高级算法
        self.init_advanced_algorithms()

    def init_advanced_algorithms(self):
        """初始化高级算法"""
        # 检查高级算法是否可用
        if 'ADVANCED_ALGORITHMS_AVAILABLE' in globals() and ADVANCED_ALGORITHMS_AVAILABLE:
            self.logger.info("高级算法模块可用，初始化高级算法...")

            # 初始化NSGA-II多目标优化算法
            self.nsga2_optimizer = NSGA2Optimizer(
                population_size=self.population_size,
                crossover_rate=self.crossover_rate,
                mutation_rate=self.mutation_rate,
                phenotype_limit=self.phenotype_limit,
                elimination_size=self.elimination_size
            )

            # 初始化约束满足算法
            self.constraint_solver = ConstraintSolver()

            # 初始化强化学习算法
            self.rl_scheduler = RLScheduler(
                learning_rate=params.get('rl_learning_rate', 0.1),
                discount_factor=params.get('rl_discount_factor', 0.9),
                exploration_rate=params.get('rl_exploration_rate', 0.1),
                exploration_decay=params.get('rl_exploration_decay', 0.995),
                min_exploration_rate=params.get('rl_min_exploration_rate', 0.01)
            )

            self.logger.info("高级算法初始化完成")
        else:
            self.logger.info("高级算法模块不可用，将使用基本算法")

    def parse_time(self, time_str):
        """解析时间字符串为datetime.time对象"""
        return datetime.strptime(time_str, "%H:%M").time()

    def _create_initial_time_slots(self):
        """创建初始的时间段列表，默认为睡眠活动"""
        try:
            initial_slots = []
            for slot_idx in range(self.time_slots_per_day):
                # 计算时间段
                slot_hours = (slot_idx * self.time_interval_minutes) // 60
                slot_minutes = (slot_idx * self.time_interval_minutes) % 60
                end_hours = ((slot_idx + 1) * self.time_interval_minutes) // 60
                end_minutes = ((slot_idx + 1) * self.time_interval_minutes) % 60

                # 处理跨天的情况
                is_overnight = False
                if end_hours >= 24:
                    is_overnight = True
                    display_end_hours = end_hours - 24
                else:
                    display_end_hours = end_hours

                # 如果是跨天，添加标记
                time_slot_str = f"{slot_hours:02d}:{slot_minutes:02d}-{display_end_hours:02d}:{end_minutes:02d}"
                if is_overnight:
                    # 在日志中添加跨天标记，但不改变时间格式
                    self.logger.debug(f"创建跨天时间段: {time_slot_str} (跨天)")

                # 默认为睡眠活动
                entry = self.create_schedule_entry(
                    time_str=time_slot_str,
                    activity_name="睡眠",
                    activity_id="10",
                    caregiver_name="None",
                    caregiver_id="0"
                )
                initial_slots.append(entry)

            self.logger.debug(f"创建了 {len(initial_slots)} 个初始时间段条目")
            return initial_slots
        except Exception as e:
            self.logger.error(f"创建初始时间段失败: {str(e)}")
            # 返回一个空列表，避免程序崩溃
            return []

    def _validate_and_fix_schedule_data(self, schedule_entries):
        """验证和修复排班数据，确保数据类型一致性

        Args:
            schedule_entries: 排班数据，可能是列表、字典或字符串

        Returns:
            验证和修复后的排班数据，始终是列表类型
        """
        try:
            # 如果是字符串，尝试解析为JSON
            if isinstance(schedule_entries, str):
                self.logger.warning(f"排班数据是字符串类型，尝试解析为JSON")
                import json
                try:
                    schedule_entries = json.loads(schedule_entries)
                except json.JSONDecodeError:
                    self.logger.error(f"无法解析排班数据为JSON: {schedule_entries[:100]}...")
                    return self._create_initial_time_slots()

            # 确保是列表类型
            if not isinstance(schedule_entries, list):
                self.logger.warning(f"排班数据不是列表类型，转换为列表")
                schedule_entries = [schedule_entries] if schedule_entries else []

            # 确保每个条目都是字典类型
            for i, entry in enumerate(schedule_entries):
                if not isinstance(entry, dict):
                    self.logger.warning(f"条目 {i} 不是字典类型，替换为默认条目")
                    schedule_entries[i] = {
                        "Activity": "睡眠",
                        "TimeSlot": f"{i//3:02d}:{(i%3)*20:02d}-{i//3:02d}:{((i%3)+1)*20:02d}",
                        "Caregiver": "None",
                        "ActivityId": "10",
                        "CaregiverId": "0"
                    }
                else:
                    # 确保必要的字段存在
                    if 'TimeSlot' not in entry:
                        entry['TimeSlot'] = f"{i//3:02d}:{(i%3)*20:02d}-{i//3:02d}:{((i%3)+1)*20:02d}"
                    if 'Activity' not in entry:
                        entry['Activity'] = "睡眠"
                    if 'ActivityId' not in entry:
                        entry['ActivityId'] = "10"
                    if 'Caregiver' not in entry:
                        entry['Caregiver'] = "None"
                    if 'CaregiverId' not in entry:
                        entry['CaregiverId'] = "0"

            # 检查条目数量
            if len(schedule_entries) != self.time_slots_per_day:
                self.logger.warning(f"条目数量 {len(schedule_entries)} 不等于时间段数量 {self.time_slots_per_day}，调整数量")

                # 如果条目数量少于时间段数量，添加默认条目
                if len(schedule_entries) < self.time_slots_per_day:
                    for i in range(len(schedule_entries), self.time_slots_per_day):
                        hours = (i * self.time_interval_minutes) // 60
                        minutes = (i * self.time_interval_minutes) % 60
                        end_hours = ((i + 1) * self.time_interval_minutes) // 60
                        end_minutes = ((i + 1) * self.time_interval_minutes) % 60

                        # 处理跨天的情况
                        is_overnight = False
                        if end_hours >= 24:
                            is_overnight = True
                            display_end_hours = end_hours - 24
                        else:
                            display_end_hours = end_hours

                        time_slot_str = f"{hours:02d}:{minutes:02d}-{display_end_hours:02d}:{end_minutes:02d}"
                        if is_overnight:
                            self.logger.debug(f"创建跨天时间段: {time_slot_str} (跨天)")

                        schedule_entries.append({
                            "Activity": "睡眠",
                            "TimeSlot": time_slot_str,
                            "Caregiver": "None",
                            "ActivityId": "10",
                            "CaregiverId": "0"
                        })
                # 如果条目数量多于时间段数量，截取前面的条目
                elif len(schedule_entries) > self.time_slots_per_day:
                    schedule_entries = schedule_entries[:self.time_slots_per_day]

            # 按时间排序
            schedule_entries.sort(key=lambda x: x['TimeSlot'])

            return schedule_entries
        except Exception as e:
            self.logger.error(f"验证和修复排班数据时出错: {str(e)}")
            # 如果出错，创建新的默认条目列表
            return self._create_initial_time_slots()

    def create_schedule_entry(self, time_str, activity_name, activity_id, caregiver_name, caregiver_id):
        """创建时间表条目，与原接口格式保持一致

        Args:
            time_str: 时间字符串，格式为'HH:MM'
            activity_name: 活动名称
            activity_id: 活动ID
            caregiver_name: 护理员姓名
            caregiver_id: 护理员ID

        Returns:
            包含时间段信息的字典
        """
        try:
            # 解析时间字符串
            if '-' in time_str:
                # 如果已经是完整的时间段格式，直接使用
                time_slot = time_str
            else:
                # 如果只是开始时间，计算结束时间
                hours, minutes = map(int, time_str.split(':'))

                # 计算结束时间（根据配置的时间间隔）
                end_minutes = minutes + self.time_interval_minutes
                end_hours = hours
                if end_minutes >= 60:
                    end_minutes -= 60
                    end_hours += 1
                # 处理跨天的情况
                is_overnight = False
                if end_hours >= 24:
                    is_overnight = True
                    display_end_hours = end_hours - 24
                else:
                    display_end_hours = end_hours

                # 记录日志
                log_message = f"创建时间段: {hours:02d}:{minutes:02d}-{display_end_hours:02d}:{end_minutes:02d}, 间隔: {self.time_interval_minutes} 分钟"
                if is_overnight:
                    log_message += " (跨天)"
                self.logger.debug(log_message)

                # 格式化时间段字符串
                time_slot = f"{hours:02d}:{minutes:02d}-{display_end_hours:02d}:{end_minutes:02d}"

            # 创建与原接口格式一致的条目
            return {
                "Activity": activity_name,
                "TimeSlot": time_slot,
                "Caregiver": caregiver_name,
                "ActivityId": activity_id,
                "CaregiverId": caregiver_id
            }
        except Exception as e:
            self.logger.error(f"创建时间段条目失败: {str(e)}")
            # 返回一个默认的条目，避免程序崩溃
            return {
                "Activity": "睡眠",
                "TimeSlot": "00:00-00:30",
                "Caregiver": "None",
                "ActivityId": "10",
                "CaregiverId": "0"
            }

    def calculate_shift_times(self):
        """根据当前配置计算每个班次的实际时间"""
        # 获取当前激活的班制模式
        active_mode = self.active_mode

        # 确定要使用的班次列表
        if active_mode == 'custom':
            if hasattr(self, 'custom_shifts') and self.custom_shifts:
                active_shifts = self.custom_shifts
            else:
                active_shifts = self.shift_mode_config['three_shift']['shifts']  # 默认使用三班制
        else:
            active_shifts = self.shift_mode_config[active_mode]['shifts']

        # 过滤启用的班次
        enabled_shifts = [shift for shift in active_shifts if shift in self.shift_control['enabled_shifts']]

        # 如果没有启用的班次，使用默认的三班制
        if not enabled_shifts:
            self.logger.warning("没有启用的班次，使用默认的三班制")
            enabled_shifts = self.shift_mode_config['three_shift']['shifts']

        # 计算每个班次的时长
        total_shifts = len(enabled_shifts)
        if total_shifts == 0:
            self.logger.warning("没有激活的班次，无法计算班次时间")
            return {}

        # 如果自动调整时间，则平均分配24小时
        if self.shift_control['auto_adjust_time']:
            hours_per_shift = 24 / total_shifts
            self.logger.info(f"自动调整时间，每个班次 {hours_per_shift} 小时")
        else:
            # 否则使用默认时长
            hours_per_shift = 8  # 默认每班8小时
            self.logger.info(f"使用默认时长，每个班次 {hours_per_shift} 小时")

        # 计算每个班次的开始和结束时间
        start_time_str = self.shift_config[enabled_shifts[0]]['start_time']
        start_time = datetime.strptime(start_time_str, "%H:%M")

        shift_times = {}
        current_time = start_time

        for shift in enabled_shifts:
            shift_start = current_time.strftime("%H:%M")

            # 计算结束时间
            current_time = current_time + timedelta(hours=hours_per_shift)
            shift_end = current_time.strftime("%H:%M")

            shift_times[shift] = {
                'start_time': shift_start,
                'end_time': shift_end,
                'duration': hours_per_shift,
                'enabled': True  # 默认启用
            }

        self.current_shift_times = shift_times
        self.logger.info(f"计算班次时间: {shift_times}")
        return shift_times

    def set_shift_mode(self, mode, custom_shifts=None, start_time=None):
        """设置班制模式

        Args:
            mode: 班制模式名称，可以是字符串('one_shift', 'two_shift', 'three_shift', 'custom')
                  或者数字(1, 2, 3)，分别表示一班制、二班制、三班制
            custom_shifts: 自定义模式下的班次列表
            start_time: 第一个班次的开始时间
        """
        # 记录原始班制模式
        original_mode = self.active_mode
        self.logger.info(f"原始班制模式: {original_mode}")

        # 处理数字班制参数
        if isinstance(mode, int) or (isinstance(mode, str) and mode.isdigit()):
            # 将数字转换为整数
            shift_number = int(mode)
            # 根据数字映射到班制模式
            if shift_number == 1:
                mode = 'one_shift'
                self.logger.info(f"数字班制参数 {shift_number} 映射到一班制")
            elif shift_number == 2:
                mode = 'two_shift'
                self.logger.info(f"数字班制参数 {shift_number} 映射到二班制")
            elif shift_number == 3:
                mode = 'three_shift'
                self.logger.info(f"数字班制参数 {shift_number} 映射到三班制")
            elif shift_number == 4:
                mode = 'custom'
                self.logger.info(f"数字班制参数 {shift_number} 映射到自定义班制")
            else:
                self.logger.warning(f"无效的数字班制参数: {shift_number}，使用默认的三班制")
                mode = 'three_shift'

        # 检查班制模式是否有效
        if mode not in self.shift_mode_config:
            self.logger.warning(f"无效的班制模式: {mode}，使用默认的三班制")
            mode = 'three_shift'

        # 更新激活模式
        self.active_mode = mode
        self.logger.info(f"设置班制模式为: {mode}")

        # 根据班制模式更新启用的班次
        if mode in self.shift_mode_config and mode != 'custom':
            # 使用配置文件中定义的班次
            self.shift_control['enabled_shifts'] = self.shift_mode_config[mode]['shifts']
            self.logger.info(f"{self.shift_mode_config[mode]['name']}模式，启用班次: {self.shift_control['enabled_shifts']}")
        elif mode == 'custom':
            # 自定义模式
            if custom_shifts:
                # 处理数字班次
                if isinstance(custom_shifts, list) and all(isinstance(item, int) or (isinstance(item, str) and item.isdigit()) for item in custom_shifts):
                    # 将数字班次转换为班次标识符
                    number_to_shift = {
                        1: 'morning_shift',
                        2: 'afternoon_shift',
                        3: 'night_shift'
                    }

                    shift_list = []
                    for number in custom_shifts:
                        number = int(number)
                        if number in number_to_shift:
                            shift_list.append(number_to_shift[number])
                        else:
                            self.logger.warning(f"无效的班次编号: {number}，已跳过")

                    self.logger.info(f"将数字班次 {custom_shifts} 转换为班次标识符: {shift_list}")
                    custom_shifts = shift_list

                # 验证自定义班次
                valid_shifts = []
                for shift in custom_shifts:
                    if shift in self.shift_config:
                        valid_shifts.append(shift)
                    else:
                        self.logger.warning(f"无效的班次: {shift}，已跳过")

                if valid_shifts:
                    self.shift_control['enabled_shifts'] = valid_shifts
                    self.custom_shifts = valid_shifts
                    self.logger.info(f"自定义模式，启用班次: {valid_shifts}")
                else:
                    self.logger.warning(f"没有有效的自定义班次，使用默认的三班制")
                    self.shift_control['enabled_shifts'] = self.shift_mode_config['three_shift']['shifts']
                    self.logger.info(f"回退到三班制模式，启用班次: {self.shift_control['enabled_shifts']}")
            else:
                # 如果没有提供自定义班次，尝试从全局参数中获取
                from config_params import three_shift_params
                global_custom_shifts = three_shift_params.get('custom_shifts')

                if global_custom_shifts:
                    self.logger.info(f"使用全局参数中的自定义班次: {global_custom_shifts}")
                    # 递归调用自己，处理全局自定义班次
                    return self.set_shift_mode(mode, global_custom_shifts, start_time)
                else:
                    self.logger.warning(f"自定义模式下没有提供班次列表，使用默认的三班制")
                    self.shift_control['enabled_shifts'] = self.shift_mode_config['three_shift']['shifts']
                    self.logger.info(f"回退到三班制模式，启用班次: {self.shift_control['enabled_shifts']}")
        else:
            # 如果模式无效，使用默认的三班制
            self.logger.warning(f"无法识别的班制模式: {mode}，使用默认的三班制")
            self.shift_control['enabled_shifts'] = self.shift_mode_config['three_shift']['shifts']
            self.logger.info(f"回退到三班制模式，启用班次: {self.shift_control['enabled_shifts']}")

        # 如果提供了开始时间，更新第一个班次的开始时间
        if start_time:
            try:
                # 验证时间格式
                datetime.strptime(start_time, "%H:%M")

                # 获取第一个启用的班次
                if self.shift_control['enabled_shifts']:
                    first_shift = self.shift_control['enabled_shifts'][0]
                    self.shift_config[first_shift]['start_time'] = start_time
                    self.logger.info(f"设置第一个班次 {first_shift} 的开始时间为: {start_time}")
            except ValueError:
                self.logger.error(f"无效的时间格式: {start_time}，请使用HH:MM格式")

        # 重新计算班次时间
        self.calculate_shift_times()
        self.logger.info(f"班制模式设置完成，班次时间: {self.current_shift_times}")

        # 检查班制模式是否有变化
        if original_mode != self.active_mode:
            self.logger.info(f"班制模式已从 {original_mode} 变更为 {self.active_mode}")

        return {
            'mode': self.active_mode,
            'name': self.shift_mode_config[self.active_mode]['name'],
            'enabled_shifts': self.shift_control['enabled_shifts'],
            'shift_times': self.current_shift_times
        }

    def update_shift_config(self, enabled_shifts=None):
        """更新班次配置

        Args:
            enabled_shifts: 启用的班次列表，例如 ['morning_shift', 'afternoon_shift']
        """
        if enabled_shifts is not None:
            # 验证班次名称
            valid_shifts = set(self.shift_config.keys())
            for shift in enabled_shifts:
                if shift not in valid_shifts:
                    raise ValueError(f"无效的班次名称: {shift}")

            # 更新启用的班次
            self.shift_control['enabled_shifts'] = enabled_shifts

            # 重新计算班次时间
            self.calculate_shift_times()

            self.logger.info(f"更新班次配置: {self.shift_control}")

    def update_shift_control(self, allow_empty_shifts=None, min_shifts=None, max_shifts=None, auto_adjust_time=None):
        """更新班次控制参数

        Args:
            allow_empty_shifts: 是否允许空班次
            min_shifts: 最小班次数
            max_shifts: 最大班次数
            auto_adjust_time: 是否自动调整班次时间
        """
        if allow_empty_shifts is not None:
            self.shift_control['allow_empty_shifts'] = allow_empty_shifts

        if min_shifts is not None:
            self.shift_control['min_shifts_per_day'] = min_shifts

        if max_shifts is not None:
            self.shift_control['max_shifts_per_day'] = max_shifts

        if auto_adjust_time is not None:
            self.shift_control['auto_adjust_time'] = auto_adjust_time
            # 如果更改了自动调整时间设置，重新计算班次时间
            self.calculate_shift_times()

        self.logger.info(f"更新班次控制参数: {self.shift_control}")

    def disable_shifts(self, shifts_to_disable):
        """临时禁用特定班次（不排班但保留时间段）

        Args:
            shifts_to_disable: 要禁用的班次列表
        """
        # 验证班次名称
        active_shifts = list(self.current_shift_times.keys())

        for shift in shifts_to_disable:
            if shift not in active_shifts:
                self.logger.warning(f"班次 {shift} 不在当前激活的班次列表中")

        # 更新班次启用状态
        for shift in self.current_shift_times:
            self.current_shift_times[shift]['enabled'] = shift not in shifts_to_disable

        self.logger.info(f"禁用班次: {shifts_to_disable}, 更新后的班次时间: {self.current_shift_times}")

    def calculate_shift_hours(self, shift_type):
        """计算班次的工作时长（小时）

        Args:
            shift_type: 班次类型

        Returns:
            float: 工作时长（小时）
        """
        # 如果班次在当前计算的时间中
        if shift_type in self.current_shift_times:
            return self.current_shift_times[shift_type]['duration']

        # 否则使用原始配置计算
        if shift_type not in self.shift_config:
            self.logger.warning(f"未知的班次类型: {shift_type}")
            return 8  # 默认返8小时

        start_time = self.parse_time(self.shift_config[shift_type]['start_time'])
        end_time = self.parse_time(self.shift_config[shift_type]['end_time'])

        # 处理跨天情况
        if end_time < start_time:
            hours = (24 - start_time.hour - start_time.minute / 60) + (end_time.hour + end_time.minute / 60)
        else:
            hours = (end_time.hour - start_time.hour) + (end_time.minute - start_time.minute) / 60

        return hours

    def check_rest_time_constraints(self, caregiver_id, target_date, target_shift):
        """检查护理员的休息时间约束

        Args:
            caregiver_id: 护理员ID
            target_date: 目标排班日期
            target_shift: 目标班次

        Returns:
            bool: 是否满足休息时间约束
        """
        # 获取护理员最近的排班记录
        recent_assignments = ShiftAssignment.query.filter(
            ShiftAssignment.caregiver_id == caregiver_id,
            ShiftAssignment.shift_date < target_date,
            ShiftAssignment.shift_date >= target_date - timedelta(days=7)  # 查看过去7天
        ).order_by(ShiftAssignment.shift_date.desc()).all()

        if not recent_assignments:
            # 没有最近的排班记录，可以安排任何班次
            return True

        # 获取最近一次的排班
        last_assignment = recent_assignments[0]
        last_shift = last_assignment.shift_type
        last_date = last_assignment.shift_date
        last_end_time = last_assignment.shift_end_time

        # 计算两个班次之间的时间间隔（小时）
        if target_shift not in self.current_shift_times:
            self.logger.warning(f"目标班次 {target_shift} 不在当前班次时间中")
            return True  # 如果目标班次不在当前班次时间中，允许安排

        target_start_time_str = self.current_shift_times[target_shift]['start_time']
        target_start_time = self.parse_time(target_start_time_str)
        target_start_datetime = datetime.combine(target_date, target_start_time)

        last_end_datetime = datetime.combine(last_date, last_end_time)

        # 处理跨天情况
        if last_end_time < self.parse_time("06:00"):
            last_end_datetime = datetime.combine(last_date + timedelta(days=1), last_end_time)

        hours_between = (target_start_datetime - last_end_datetime).total_seconds() / 3600

        # 检查基本休息时间
        min_rest = self.rest_time_control['min_rest_between_shifts']

        # 如果上一个班次是夜班，需要更长的休息时间
        if last_shift == '班次3' or last_shift == 'night_shift':
            min_rest = self.rest_time_control['min_rest_after_night_shift']

        # 检查班次转换休息时间
        # 将班次名称映射到内部名称
        shift_name_map = {
            '班次1': 'morning_shift',
            '班次2': 'afternoon_shift',
            '班次3': 'night_shift'
        }

        last_shift_internal = shift_name_map.get(last_shift, last_shift)
        target_shift_internal = shift_name_map.get(target_shift, target_shift)

        transition_key = f"{last_shift_internal}_to_{target_shift_internal}"
        if transition_key in self.rest_time_control['shift_transition_rest']:
            transition_rest = self.rest_time_control['shift_transition_rest'][transition_key]
            min_rest = max(min_rest, transition_rest)

        # 检查是否满足最小休息时间要求
        if hours_between < min_rest:
            self.logger.info(f"护理员 {caregiver_id} 不满足休息时间要求: 需要 {min_rest} 小时，实际 {hours_between:.2f} 小时")
            return False

        # 检查连续工作班次数
        consecutive_shifts = 1  # 从最近的一个班次开始计数
        current_date = last_date
        for assignment in recent_assignments[1:]:
            if (current_date - assignment.shift_date).days <= 1:  # 连续的日期
                consecutive_shifts += 1
                current_date = assignment.shift_date
            else:
                break

        if consecutive_shifts >= self.rest_time_control['max_consecutive_shifts']:
            self.logger.info(f"护理员 {caregiver_id} 已经连续工作 {consecutive_shifts} 个班次，超过最大限制")
            return False

        # 检查每周工作时间
        weekly_work_hours = 0
        for assignment in recent_assignments:
            # 计算班次工作时长
            shift_hours = self.calculate_shift_hours(assignment.shift_type)
            weekly_work_hours += shift_hours

        # 加上目标班次的工作时长
        target_shift_hours = self.calculate_shift_hours(target_shift)
        if weekly_work_hours + target_shift_hours > self.rest_time_control['max_weekly_work_hours']:
            self.logger.info(f"护理员 {caregiver_id} 每周工作时间将超过限制: {weekly_work_hours + target_shift_hours} > {self.rest_time_control['max_weekly_work_hours']}")
            return False

        # 检查每周夜班次数
        if target_shift == '班次3' or target_shift == 'night_shift':
            weekly_night_shifts = sum(1 for a in recent_assignments if a.shift_type == '班次3' or a.shift_type == 'night_shift')
            if weekly_night_shifts + 1 > self.rest_time_control['max_weekly_night_shifts']:
                self.logger.info(f"护理员 {caregiver_id} 每周夜班次数将超过限制: {weekly_night_shifts + 1} > {self.rest_time_control['max_weekly_night_shifts']}")
                return False

        # 检查周末休息要求
        if self.rest_time_control['require_weekend_rest']:
            target_weekday = target_date.weekday()
            if target_weekday >= 5:  # 5=周六，6=周日
                # 检查本月已经安排的周末班次数
                month_start = target_date.replace(day=1)
                next_month = target_date.replace(day=28) + timedelta(days=4)
                month_end = next_month.replace(day=1) - timedelta(days=1)

                weekend_shifts = ShiftAssignment.query.filter(
                    ShiftAssignment.caregiver_id == caregiver_id,
                    ShiftAssignment.shift_date >= month_start,
                    ShiftAssignment.shift_date <= month_end,
                    func.extract('dow', ShiftAssignment.shift_date).in_([5, 6])  # 周六和周日
                ).count()

                if weekend_shifts >= self.rest_time_control['max_weekend_shifts_per_month']:
                    self.logger.info(f"护理员 {caregiver_id} 本月周末班次数将超过限制: {weekend_shifts + 1} > {self.rest_time_control['max_weekend_shifts_per_month']}")
                    return False

        return True

    def get_caregivers(self):
        """获取护理员信息，限制数量"""
        from config_params import params

        all_caregivers = Attendantskills.query.all()

        # 使用与原程序相同的参数限制数量
        limit = params.get('caregiver_num', len(all_caregivers))
        caregivers = all_caregivers[:limit]

        self.logger.info(f"从数据库中获取到 {len(all_caregivers)} 个护理员，使用 {len(caregivers)} 个")
        return caregivers

    def get_elders(self):
        """获取需要安排照护的老人信息，限制数量"""
        from config_params import params

        all_elders = Elderdemand.query.filter_by(arrange='1').all()

        # 使用与原程序相同的参数限制数量
        limit = params.get('client_num', len(all_elders))

        # 按照老人编号分组，确保获取不同的老人
        distinct_elders = {}
        for elder in all_elders:
            if elder.elder not in distinct_elders:
                distinct_elders[elder.elder] = elder

        # 将不同的老人转换为列表
        distinct_elders_list = list(distinct_elders.values())

        # 限制老人数量
        elders = distinct_elders_list[:limit]

        self.logger.info(f"从数据库中获取到 {len(all_elders)} 个需要安排的老人，{len(distinct_elders)} 个不同老人，使用 {len(elders)} 个")
        return elders

    def get_nursing_actions(self):
        """获取所有护理动作信息"""
        actions = DicNursingAction.query.all()
        actions_dict = {action.id: action for action in actions}
        self.logger.info(f"从数据库中获取到 {len(actions_dict)} 个护理动作")
        return actions_dict

    def parse_skills(self, skills_str):
        """解析技能字符串为技能ID列表"""
        if not skills_str:
            return []
        try:
            return [int(skill.strip()) for skill in skills_str.split(',') if skill.strip()]
        except ValueError as e:
            self.logger.error(f"解析技能字符串失败: {skills_str}, 错误: {str(e)}")
            return []

    def get_shift_hours(self, shift_type):
        """计算班次的工作时长（小时）

        Args:
            shift_type: 班次类型

        Returns:
            float: 工作时长（小时），如果班次不存在则返回0
        """
        # 检查班次是否在当前启用的班次中
        if shift_type not in self.shift_control['enabled_shifts']:
            self.logger.debug(f"班次 {shift_type} 不在当前启用的班次中，返回0小时")
            return 0

        # 使用calculate_shift_hours方法计算工作时长
        return self.calculate_shift_hours(shift_type)

    def calculate_required_caregivers(self, total_caregivers):
        """计算每个班次所需的护理员数量

        改进的算法，确保：
        1. 每个班次都至少有一个护理员（即使总人数很少）
        2. 允许护理员轮班，同一个护理员可以在同一天内担任多个班次
        3. 保留预留资源的概念，但不影响基本的班次覆盖
        """
        # 获取当前启用的班次
        active_shifts = self.shift_control['enabled_shifts']
        self.logger.info(f"当前启用的班次: {active_shifts}")

        # 初始化结果字典，默认每个班次都不需要护理员
        # 只为启用的班次初始化
        result = {}
        for shift in active_shifts:
            result[shift] = 0  # 默认为0

        self.logger.info(f"初始化班次分配结果字典: {result}")

        # 计算各班次工作时长，只计算启用的班次
        shift_hours = {}
        total_hours = 0
        for shift in active_shifts:
            hours = self.get_shift_hours(shift)
            shift_hours[shift] = hours
            total_hours += hours
            self.logger.info(f"{self.shift_config[shift]['name']}工作时长: {hours} 小时")

        # 计算各班次所需人数（考虑预留资源）
        available_caregivers = max(1, int(total_caregivers * (1 - self.reserve_resource_ratio)))
        self.logger.info(f"总护理员数量: {total_caregivers}, 可用护理员数量: {available_caregivers}")

        # 如果可用护理员少于启用班次数，启用轮班模式
        if available_caregivers < len(active_shifts):
            self.logger.info(f"可用护理员少于启用班次数，启用轮班模式")

            # 确保每个启用的班次至少有一个人
            for shift in active_shifts:
                result[shift] = 1

            self.logger.info(f"轮班模式下的班次分配结果: {result}")
            return result

        # 如果护理员足够，按比例分配
        if total_hours > 0:
            # 按工作时长比例分配护理员
            for shift in active_shifts:
                ratio = shift_hours[shift] / total_hours
                result[shift] = max(1, int(available_caregivers * ratio))
        else:
            # 如果没有工作时长信息，均匀分配
            caregivers_per_shift = max(1, available_caregivers // len(active_shifts))
            for shift in active_shifts:
                result[shift] = caregivers_per_shift

        # 确保总人数不超过可用护理员数量
        total_assigned = sum(result.values())

        if total_assigned > available_caregivers:
            # 如果总人数超过可用人数，按比例减少
            excess = total_assigned - available_caregivers

            # 从人数最多的班次开始减少
            shifts = [(shift, result[shift]) for shift in active_shifts]
            # 先按人数排序，人数相同时按班次顺序排序
            shifts.sort(key=lambda x: (-x[1], self.shift_config[x[0]]['order']))

            for i in range(excess):
                shift_name = shifts[i % len(shifts)][0]
                if result[shift_name] > 1:
                    result[shift_name] -= 1

        self.logger.info(f"班次分配结果: {result}, 总分配人数: {sum(result.values())}")
        return result

    def adjust_shift_times(self, shift_start_time=None):
        """调整班次时间配置，确保班次时间完全连续无空档

        改进的算法，确保：
        1. 班次时间完全连续，无空档
        2. 根据班制模式自动调整班次时长
        3. 正确处理夜班跨天情况
        """
        # 获取当前启用的班次
        active_shifts = self.shift_control['enabled_shifts']
        self.logger.info(f"当前启用的班次: {active_shifts}")

        # 如果没有启用的班次，使用默认的三班制
        if not active_shifts:
            self.logger.warning("没有启用的班次，使用默认的三班制")
            active_shifts = self.shift_mode_config['three_shift']['shifts']

        # 计算每个班次的时长
        total_shifts = len(active_shifts)
        if total_shifts == 0:
            self.logger.warning("没有启用的班次，无法调整班次时间")
            return {}

        # 根据班制模式计算每个班次的时长
        hours_per_shift = 24 / total_shifts
        self.logger.info(f"每个班次的时长: {hours_per_shift} 小时")

        # 如果没有指定起始时间，使用默认的第一个班次开始时间
        if not shift_start_time:
            first_shift = active_shifts[0]
            shift_start_time = self.shift_config[first_shift]['start_time']
            self.logger.info(f"使用默认的第一个班次开始时间: {shift_start_time}")

        # 解析起始时间
        start_time = self.parse_time(shift_start_time)
        self.logger.info(f"第一个班次开始时间: {start_time}")

        # 计算每个班次的开始和结束时间
        adjusted_config = {}
        current_time_dt = datetime.combine(date.today(), start_time)

        for i, shift in enumerate(active_shifts):
            shift_start = current_time_dt.time()
            shift_start_str = shift_start.strftime("%H:%M")

            # 计算结束时间
            current_time_dt += timedelta(hours=hours_per_shift)
            shift_end = current_time_dt.time()
            shift_end_str = shift_end.strftime("%H:%M")

            # 处理跨天情况
            is_overnight = current_time_dt.date() > date.today()

            # 记录日志
            log_message = f"{shift} 时间: {shift_start_str} - {shift_end_str}"
            if is_overnight:
                log_message += " (跨天)"
            self.logger.info(log_message)

            # 添加到配置中
            adjusted_config[shift] = {
                'start_time': shift_start_str,
                'end_time': shift_end_str,
                'name': self.shift_config[shift]['name'] if shift in self.shift_config else f"班次{i+1}"
            }

        # 检查班次时间是否连续
        self.logger.info(f"调整后的班次时间配置: {adjusted_config}")
        return adjusted_config

    def match_caregiver_to_elder_needs(self, caregivers, elder_actions):
        """根据老人需求匹配最合适的护理员

        匹配策略：
        1. 首先考虑技能匹配度（能够满足老人需求的技能数量）
        2. 其次考虑护理员的工作负荷（已分配的时间段数量）
        3. 最后考虑护理员的技能多样性（避免浪费高技能护理员）
        """
        # 将老人需要的护理动作转换为整数列表
        elder_action_ids = self.parse_skills(elder_actions)
        if not elder_action_ids:
            self.logger.warning(f"老人需求为空：{elder_actions}")
            return None

        # 记录所有匹配的护理员及其分数
        matched_caregivers = []

        for caregiver in caregivers:
            # 解析护理员技能
            caregiver_skills = self.parse_skills(caregiver.skills)
            if not caregiver_skills:
                self.logger.warning(f"护理员 {caregiver.attendant_name} 没有技能记录")
                continue

            # 计算匹配分数（匹配的技能数量）
            match_score = sum(1 for action in elder_action_ids if action in caregiver_skills)

            # 如果有匹配的技能，则添加到候选列表
            if match_score > 0:
                # 计算护理员的工作负荷（已分配的时间段数量）
                workload = TimeSlot.query.filter_by(
                    caregiver_id=caregiver.id,
                    schedule_date=date.today()
                ).count()

                # 计算技能多样性分数（技能数量与匹配分数的比值）
                # 这个分数越高，表示护理员有更多不需要的技能，应该优先保留给更需要的老人
                skill_diversity = len(caregiver_skills) / max(match_score, 1)

                matched_caregivers.append({
                    'caregiver': caregiver,
                    'match_score': match_score,
                    'workload': workload,
                    'skill_diversity': skill_diversity
                })

        if not matched_caregivers:
            self.logger.warning(f"没有找到匹配的护理员来满足老人需求: {elder_actions}")
            # 如果没有匹配的护理员，但有可用的护理员，则随机选择一个
            if caregivers:
                random_caregiver = random.choice(caregivers)
                self.logger.warning(f"随机选择护理员 {random_caregiver.attendant_name}")
                return random_caregiver
            return None

        # 按匹配分数降序排序
        matched_caregivers.sort(key=lambda x: (-x['match_score'], x['workload'], x['skill_diversity']))

        # 返回最佳匹配
        best_match = matched_caregivers[0]['caregiver']
        self.logger.info(f"为老人需求 {elder_actions} 匹配到护理员 {best_match.attendant_name}")
        return best_match

    def check_caregiver_availability(self, caregiver_id, target_date, time_slot):
        """检查护理员在指定时间段是否可用

        注意：此方法已修改，不再从数据库查询，而是使用内存中的可用性数据
        """
        # 使用内存中的字典检查护理员可用性
        if not hasattr(self, '_caregiver_availability'):
            self._caregiver_availability = {}

        # 使用元组作为字典键
        key = (caregiver_id, target_date.isoformat(), time_slot)

        # 如果键不存在或者值为True，则该护理员在该时间段可用
        return self._caregiver_availability.get(key, True)

    def update_caregiver_availability(self, caregiver_id, target_date, time_slot, is_available=False):
        """更新护理员在指定时间段的可用性

        注意：此方法已修改，不再向caregiver_schedules表写入数据
        仅在内存中跟踪护理员可用性，用于排班算法内部使用
        """
        # 使用内存中的字典跟踪护理员可用性，而不是写入数据库
        if not hasattr(self, '_caregiver_availability'):
            self._caregiver_availability = {}

        # 使用元组作为字典键
        key = (caregiver_id, target_date.isoformat(), time_slot)
        self._caregiver_availability[key] = is_available

        self.logger.info(f"更新护理员 {caregiver_id} 在 {target_date} 时间段 {time_slot} 的可用性为 {is_available}（仅内存中）")

    def optimize_schedule(self, initial_schedule, caregivers):
        """使用算法优化排班方案

        复用原接口的遗传算法和模拟退火算法，优化排班方案

        Args:
            initial_schedule: 初始排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        self.logger.info("开始使用混合算法优化排班方案...")

        # 初始化种群
        population = [initial_schedule]
        for _ in range(self.population_size - 1):
            # 生成变异的排班方案
            variant = self.create_schedule_variant(initial_schedule, caregivers)
            population.append(variant)

        best_schedule = initial_schedule
        best_fitness = self.evaluate_schedule(best_schedule, caregivers)
        self.logger.info(f"初始适应度: {best_fitness}")

        # 重置模拟退火温度
        self.temperature = self.initial_temperature

        # 遗传算法迭代
        for generation in range(self.phenotype_limit):
            # 计算适应度
            fitness_values = [self.evaluate_schedule(schedule, caregivers) for schedule in population]

            # 找到最佳解
            max_index = fitness_values.index(max(fitness_values))
            if fitness_values[max_index] > best_fitness:
                best_schedule = population[max_index]
                best_fitness = fitness_values[max_index]
                self.logger.info(f"第 {generation} 代发现更好的解，适应度: {best_fitness}")

            # 选择父代
            parents = self.select_parents(population, fitness_values)

            # 交叉和变异
            new_population = []
            for i in range(0, len(parents), 2):
                if i + 1 < len(parents):
                    # 交叉
                    if random.random() < self.crossover_rate:
                        child1, child2 = self.crossover(parents[i], parents[i+1])
                        new_population.extend([child1, child2])
                    else:
                        new_population.extend([parents[i], parents[i+1]])

            # 变异
            for i in range(len(new_population)):
                if random.random() < self.mutation_rate:
                    new_population[i] = self.mutate(new_population[i], caregivers)

            # 模拟退火优化
            for i in range(len(new_population)):
                new_population[i] = self.simulated_annealing(new_population[i], caregivers)

            # 保留精英
            elite_count = self.population_size - self.elimination_size
            sorted_indices = sorted(range(len(fitness_values)), key=lambda i: fitness_values[i], reverse=True)
            elite = [population[i] for i in sorted_indices[:elite_count]]

            # 更新种群
            population = elite + new_population[:self.population_size - len(elite)]

            # 更新温度
            self.temperature *= self.cooling_rate

            if generation % 10 == 0:
                self.logger.info(f"第 {generation} 代，最佳适应度: {best_fitness}, 当前温度: {self.temperature:.2f}")

        self.logger.info(f"混合算法优化完成，最终适应度: {best_fitness}")
        return best_schedule

    def genetic_algorithm_optimize(self, initial_schedule, caregivers):
        """仅使用遗传算法优化排班方案

        Args:
            initial_schedule: 初始排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        self.logger.info("开始使用遗传算法优化排班方案...")

        # 初始化种群
        population = [initial_schedule]
        for _ in range(self.population_size - 1):
            # 生成变异的排班方案
            variant = self.create_schedule_variant(initial_schedule, caregivers)
            population.append(variant)

        best_schedule = initial_schedule
        best_fitness = self.evaluate_schedule(best_schedule, caregivers)
        self.logger.info(f"初始适应度: {best_fitness}")

        # 遗传算法迭代
        for generation in range(self.phenotype_limit):
            # 计算适应度
            fitness_values = [self.evaluate_schedule(schedule, caregivers) for schedule in population]

            # 找到最佳解
            max_index = fitness_values.index(max(fitness_values))
            if fitness_values[max_index] > best_fitness:
                best_schedule = population[max_index]
                best_fitness = fitness_values[max_index]
                self.logger.info(f"第 {generation} 代发现更好的解，适应度: {best_fitness}")

            # 选择父代
            parents = self.select_parents(population, fitness_values)

            # 交叉和变异
            new_population = []
            for i in range(0, len(parents), 2):
                if i + 1 < len(parents):
                    # 交叉
                    if random.random() < self.crossover_rate:
                        child1, child2 = self.crossover(parents[i], parents[i+1])
                        new_population.extend([child1, child2])
                    else:
                        new_population.extend([parents[i], parents[i+1]])

            # 变异
            for i in range(len(new_population)):
                if random.random() < self.mutation_rate:
                    new_population[i] = self.mutate(new_population[i], caregivers)

            # 保留精英
            elite_count = self.population_size - self.elimination_size
            sorted_indices = sorted(range(len(fitness_values)), key=lambda i: fitness_values[i], reverse=True)
            elite = [population[i] for i in sorted_indices[:elite_count]]

            # 更新种群
            population = elite + new_population[:self.population_size - len(elite)]

            # 每10代输出一次日志
            if generation % 10 == 0:
                self.logger.info(f"第 {generation} 代完成，当前最佳适应度: {best_fitness}")

        self.logger.info(f"遗传算法优化完成，最终适应度: {best_fitness}")
        return best_schedule

    def simulated_annealing_optimize(self, initial_schedule, caregivers):
        """仅使用模拟退火算法优化排班方案

        Args:
            initial_schedule: 初始排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        self.logger.info("开始使用模拟退火算法优化排班方案...")

        # 重置温度
        self.temperature = self.initial_temperature

        current = initial_schedule
        current_fitness = self.evaluate_schedule(current, caregivers)
        best_schedule = current
        best_fitness = current_fitness

        self.logger.info(f"初始适应度: {best_fitness}")

        # 记录连续无改进的次数
        no_improvement_count = 0
        max_no_improvement = 20

        # 模拟退火迭代
        for iteration in range(self.phenotype_limit * 10):  # 模拟退火需要更多的迭代
            # 生成领域解
            neighbor = self.mutate(current, caregivers)
            neighbor_fitness = self.evaluate_schedule(neighbor, caregivers)

            # 计算适应度差异
            delta = neighbor_fitness - current_fitness

            # 如果领域解更好，或者满足模拟退火接受条件，则接受领域解
            if delta > 0 or random.random() < math.exp(delta / self.temperature):
                current = neighbor
                current_fitness = neighbor_fitness

                # 如果发现更好的解，更新最佳解
                if current_fitness > best_fitness:
                    best_schedule = current
                    best_fitness = current_fitness
                    self.logger.info(f"第 {iteration} 次迭代发现更好的解，适应度: {best_fitness}")
                    no_improvement_count = 0
                else:
                    no_improvement_count += 1
            else:
                no_improvement_count += 1

            # 如果连续多次没有改进，提高温度
            if no_improvement_count >= max_no_improvement:
                self.temperature *= 1.5  # 提高温度
                self.logger.info(f"连续 {max_no_improvement} 次没有改进，提高温度至 {self.temperature:.2f}")
                no_improvement_count = 0
            else:
                # 正常冷却
                self.temperature *= self.cooling_rate

            # 每100次迭代输出一次日志
            if iteration % 100 == 0:
                self.logger.info(f"第 {iteration} 次迭代完成，当前最佳适应度: {best_fitness}, 当前温度: {self.temperature:.2f}")

            # 如果温度过低，结束迭代
            if self.temperature < 0.01:
                self.logger.info(f"温度过低 ({self.temperature:.4f})，结束迭代")
                break

        self.logger.info(f"模拟退火算法优化完成，最终适应度: {best_fitness}")
        return best_schedule

    def create_schedule_variant(self, schedule, caregivers):
        """创建排班方案的变异

        Args:
            schedule: 原始排班方案
            caregivers: 护理员列表

        Returns:
            变异的排班方案
        """
        # 深拷贝原始排班方案
        variant = {
            'shift_assignments': schedule['shift_assignments'].copy(),
            'elder_schedules': {k: v.copy() for k, v in schedule['elder_schedules'].items()},
            'time_slot_assignments': schedule['time_slot_assignments'].copy() if 'time_slot_assignments' in schedule else []
        }

        # 随机修改班次分配
        if variant['shift_assignments']:
            # 随机选择一些班次分配进行修改
            num_to_modify = max(1, int(len(variant['shift_assignments']) * 0.2))  # 修改约20%的班次分配
            indices_to_modify = random.sample(range(len(variant['shift_assignments'])), num_to_modify)

            for idx in indices_to_modify:
                # 随机选择一个新的护理员
                new_caregiver = random.choice(caregivers)
                variant['shift_assignments'][idx].caregiver_id = new_caregiver.id
                variant['shift_assignments'][idx].caregiver_name = new_caregiver.attendant_name

        return variant

    def evaluate_schedule(self, schedule, caregivers):
        """评估排班方案的质量

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            适应度分数
        """
        score = 0
        weights = {
            'coverage': 0.3,       # 班次覆盖率权重
            'balance': 0.2,        # 工作负载平衡度权重
            'skill_match': 0.2,    # 技能匹配度权重
            'preference': 0.15,    # 护理员偏好满足度权重
            'continuity': 0.15     # 排班连续性权重
        }

        # 1. 评估班次覆盖率
        coverage_score = self.evaluate_coverage(schedule)

        # 2. 评估护理员工作负载平衡度
        balance_score = self.evaluate_workload_balance(schedule, caregivers)

        # 3. 评估技能匹配度
        skill_match_score = self.evaluate_skill_match(schedule, caregivers)

        # 4. 评估护理员偏好满足度
        preference_score = self.evaluate_preference(schedule, caregivers)

        # 5. 评估排班连续性
        continuity_score = self.evaluate_continuity(schedule)

        # 加权平均计算总分
        score = (coverage_score * weights['coverage'] +
                 balance_score * weights['balance'] +
                 skill_match_score * weights['skill_match'] +
                 preference_score * weights['preference'] +
                 continuity_score * weights['continuity']) * 100

        return score

    def evaluate_coverage(self, schedule):
        """评估班次覆盖率

        Args:
            schedule: 排班方案

        Returns:
            覆盖率分数 (0-1)
        """
        if 'shift_assignments' not in schedule or not schedule['shift_assignments']:
            return 0

        # 按日期和班次类型分组
        shifts_by_date = {}
        for assignment in schedule['shift_assignments']:
            date_key = assignment.shift_date.isoformat() if hasattr(assignment.shift_date, 'isoformat') else str(assignment.shift_date)
            if date_key not in shifts_by_date:
                shifts_by_date[date_key] = set()
            shifts_by_date[date_key].add(assignment.shift_type)

        # 计算每天的覆盖率
        total_coverage = 0
        active_shifts_count = len(self.shift_control['enabled_shifts'])
        if active_shifts_count == 0:
            return 0

        for date_key, shift_types in shifts_by_date.items():
            coverage = len(shift_types) / active_shifts_count
            total_coverage += coverage

        # 计算平均覆盖率
        if shifts_by_date:
            return total_coverage / len(shifts_by_date)
        return 0

    def evaluate_workload_balance(self, schedule, caregivers):
        """评估护理员工作负载平衡度

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            平衡度分数 (0-1)
        """
        if 'shift_assignments' not in schedule or not schedule['shift_assignments'] or not caregivers:
            return 0

        # 统计每个护理员的班次数
        caregiver_shifts = {}
        for assignment in schedule['shift_assignments']:
            caregiver_id = assignment.caregiver_id
            caregiver_shifts[caregiver_id] = caregiver_shifts.get(caregiver_id, 0) + 1

        # 为所有护理员创建记录，包括未分配班次的护理员
        for caregiver in caregivers:
            if caregiver.id not in caregiver_shifts:
                caregiver_shifts[caregiver.id] = 0

        # 计算标准差
        if caregiver_shifts:
            shift_counts = list(caregiver_shifts.values())
            mean_shifts = sum(shift_counts) / len(shift_counts)

            # 避免除以零
            if mean_shifts == 0:
                return 1  # 如果所有护理员都没有班次，认为是平衡的

            std_dev = (sum((count - mean_shifts) ** 2 for count in shift_counts) / len(shift_counts)) ** 0.5

            # 标准差越小，平衡度越高
            # 使用指数衰减函数将标准差映射到0-1范围
            balance_score = math.exp(-std_dev / mean_shifts)
            return balance_score

        return 0

    def evaluate_skill_match(self, schedule, caregivers):
        """评估技能匹配度

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            技能匹配度分数 (0-1)
        """
        if 'time_slot_assignments' not in schedule or not schedule['time_slot_assignments']:
            return 0

        total_matches = 0
        total_slots = len(schedule['time_slot_assignments'])

        if total_slots == 0:
            return 0

        for slot in schedule['time_slot_assignments']:
            # 找到该时间段的护理员
            caregiver = next((c for c in caregivers if c.id == slot.caregiver_id), None)
            if caregiver:
                # 检查护理员技能是否匹配活动需求
                caregiver_skills = self.parse_skills(caregiver.skills)
                activity_id = int(slot.activity_id) if hasattr(slot, 'activity_id') and slot.activity_id and slot.activity_id.isdigit() else 0

                if activity_id in caregiver_skills:
                    total_matches += 1

        return total_matches / total_slots

    def evaluate_preference(self, schedule, caregivers):
        """评估护理员偏好满足度

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            偏好满足度分数 (0-1)
        """
        if 'shift_assignments' not in schedule or not schedule['shift_assignments']:
            return 0

        # 假设每个护理员有偏好的班次类型
        # 这里简化处理，随机生成偏好
        caregiver_preferences = {}
        for caregiver in caregivers:
            # 随机生成偏好班次类型
            preferred_shifts = random.sample(list(self.shift_control['enabled_shifts']),
                                           min(2, len(self.shift_control['enabled_shifts'])))
            caregiver_preferences[caregiver.id] = preferred_shifts

        # 计算偏好满足度
        total_matches = 0
        total_assignments = len(schedule['shift_assignments'])

        if total_assignments == 0:
            return 0

        for assignment in schedule['shift_assignments']:
            caregiver_id = assignment.caregiver_id
            if caregiver_id in caregiver_preferences and assignment.shift_type in caregiver_preferences[caregiver_id]:
                total_matches += 1

        return total_matches / total_assignments

    def evaluate_continuity(self, schedule):
        """评估排班连续性

        Args:
            schedule: 排班方案

        Returns:
            连续性分数 (0-1)
        """
        if 'shift_assignments' not in schedule or not schedule['shift_assignments']:
            return 0

        # 按护理员和日期分组
        caregiver_schedules = {}
        for assignment in schedule['shift_assignments']:
            caregiver_id = assignment.caregiver_id
            date_key = assignment.shift_date.isoformat() if hasattr(assignment.shift_date, 'isoformat') else str(assignment.shift_date)

            if caregiver_id not in caregiver_schedules:
                caregiver_schedules[caregiver_id] = {}

            if date_key not in caregiver_schedules[caregiver_id]:
                caregiver_schedules[caregiver_id][date_key] = []

            caregiver_schedules[caregiver_id][date_key].append(assignment.shift_type)

        # 计算连续工作天数
        continuity_score = 0
        total_caregivers = len(caregiver_schedules)

        if total_caregivers == 0:
            return 0

        for caregiver_id, dates in caregiver_schedules.items():
            # 按日期排序
            sorted_dates = sorted(dates.keys())

            # 计算最长连续工作天数
            max_consecutive = 0
            current_consecutive = 0

            for i in range(len(sorted_dates)):
                if i > 0:
                    # 检查是否是连续的日期
                    prev_date = datetime.strptime(sorted_dates[i-1], '%Y-%m-%d').date()
                    curr_date = datetime.strptime(sorted_dates[i], '%Y-%m-%d').date()

                    if (curr_date - prev_date).days == 1:
                        current_consecutive += 1
                    else:
                        current_consecutive = 1
                else:
                    current_consecutive = 1

                max_consecutive = max(max_consecutive, current_consecutive)

            # 理想的连续工作天数是3-5天
            if 3 <= max_consecutive <= 5:
                continuity_score += 1
            elif max_consecutive < 3:
                continuity_score += max_consecutive / 3
            else:  # max_consecutive > 5
                continuity_score += 5 / max_consecutive

        return continuity_score / total_caregivers

    def select_parents(self, population, fitness_values):
        """选择父代

        Args:
            population: 种群
            fitness_values: 适应度分数

        Returns:
            选中的父代
        """
        # 使用轮盘赋值选择
        total_fitness = sum(fitness_values)
        if total_fitness == 0:
            # 如果所有适应度都为0，则随机选择
            return random.sample(population, len(population) // 2)

        selection_probs = [f / total_fitness for f in fitness_values]
        selected_indices = random.choices(range(len(population)), weights=selection_probs, k=len(population) // 2)
        return [population[i] for i in selected_indices]

    def crossover(self, parent1, parent2):
        """交叉操作

        Args:
            parent1: 父代一
            parent2: 父代二

        Returns:
            两个子代
        """
        # 对班次分配进行交叉
        if 'shift_assignments' in parent1 and 'shift_assignments' in parent2:
            # 随机选择交叉点
            crossover_point = random.randint(1, min(len(parent1['shift_assignments']), len(parent2['shift_assignments'])) - 1)

            # 创建子代
            child1 = {
                'shift_assignments': parent1['shift_assignments'][:crossover_point] + parent2['shift_assignments'][crossover_point:],
                'elder_schedules': parent1['elder_schedules'].copy(),
                'time_slot_assignments': parent1['time_slot_assignments'].copy() if 'time_slot_assignments' in parent1 else []
            }

            child2 = {
                'shift_assignments': parent2['shift_assignments'][:crossover_point] + parent1['shift_assignments'][crossover_point:],
                'elder_schedules': parent2['elder_schedules'].copy(),
                'time_slot_assignments': parent2['time_slot_assignments'].copy() if 'time_slot_assignments' in parent2 else []
            }

            return child1, child2

        # 如果没有班次分配，直接返回父代
        return parent1, parent2

    def mutate(self, schedule, caregivers):
        """变异操作

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            变异后的排班方案
        """
        # 深拷贝排班方案
        mutated = {
            'shift_assignments': schedule['shift_assignments'].copy(),
            'elder_schedules': {k: v.copy() for k, v in schedule['elder_schedules'].items()},
            'time_slot_assignments': schedule['time_slot_assignments'].copy() if 'time_slot_assignments' in schedule else []
        }

        # 变异班次分配
        if mutated['shift_assignments']:
            # 随机选择一个班次分配进行变异
            idx = random.randint(0, len(mutated['shift_assignments']) - 1)

            # 随机选择一个新的护理员
            new_caregiver = random.choice(caregivers)
            mutated['shift_assignments'][idx].caregiver_id = new_caregiver.id
            mutated['shift_assignments'][idx].caregiver_name = new_caregiver.attendant_name

        return mutated

    def simulated_annealing(self, schedule, caregivers):
        """模拟退火优化

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        current = schedule
        current_fitness = self.evaluate_schedule(current, caregivers)

        # 进行多次尝试
        for _ in range(10):  # 限制尝试次数
            # 生成领域解
            neighbor = self.mutate(current, caregivers)
            neighbor_fitness = self.evaluate_schedule(neighbor, caregivers)

            # 计算适应度差异
            delta = neighbor_fitness - current_fitness

            # 如果领域解更好，或者满足模拟退火接受条件，则接受领域解
            if delta > 0 or random.random() < math.exp(delta / self.temperature):
                current = neighbor
                current_fitness = neighbor_fitness

        return current

    def generate_daily_schedule(self, target_date, caregivers, elders, shift_start_time=None):
        """生成单日排班，同时考虑老人需求和护理员技能

        改进的算法，实现轮班制：
        1. 确保每个班次都至少有一个护理员
        2. 允许同一个护理员在同一天内担任多个班次，但要确保有足够的休息时间
        3. 保留预留资源的概念，但不影响基本的班次覆盖
        """
        # 如果提供了开始时间，调整班次时间
        current_shift_config = self.adjust_shift_times(shift_start_time)
        self.logger.info(f"班次时间配置: {current_shift_config}")

        # 获取当前启用的班次
        active_shifts = [shift for shift in self.shift_control['enabled_shifts']
                        if shift in current_shift_config]

        if not active_shifts:
            self.logger.warning("没有启用的班次，无法生成排班")
            return {'shift_assignments': [], 'schedule_results': [], 'time_slots': []}

        self.logger.info(f"当前启用的班次: {active_shifts}")

        # 计算各班次所需人数
        required_caregivers = self.calculate_required_caregivers(len(caregivers))
        self.logger.info(f"各班次所需护理员数量: {required_caregivers}")

        # 检查是否需要启用轮班模式
        total_required = sum(required_caregivers.values())
        is_rotation_mode = total_required > len(caregivers)

        if is_rotation_mode:
            self.logger.info(f"所需护理员总数 {total_required} 超过可用护理员数量 {len(caregivers)}，启用轮班模式")

        # 根据排班次数排序护理员，优先分配排班次数少的护理员
        # 这样可以确保预留资源不会总是同一个人
        if hasattr(self, '_caregiver_assignment_count'):
            sorted_caregivers = sorted(caregivers, key=lambda c: self._caregiver_assignment_count.get(c.id, 0))
        else:
            # 如果还没有跟踪数据，则随机排序
            sorted_caregivers = list(caregivers)
            random.shuffle(sorted_caregivers)

        # 跟踪已分配班次的护理员，确保同一天不会分配过多班次
        assigned_caregivers = {}

        # 班次分配结果
        shift_assignments = []

        # 为每个班次分配护理员
        # 只为启用的班次分配护理员
        shift_types = active_shifts.copy()
        # 根据 order 字段排序
        shift_types.sort(key=lambda x: self.shift_config[x]['order'])
        for shift_type in shift_types:
            num_required = required_caregivers[shift_type]
            # 获取中文班次名称
            shift_name_cn = self.shift_config[shift_type]['name']
            self.logger.info(f"开始为 {shift_name_cn} 分配 {num_required} 个护理员")

            # 获取班次时间
            shift_start = self.parse_time(current_shift_config[shift_type]['start_time'])
            shift_end = self.parse_time(current_shift_config[shift_type]['end_time'])

            # 处理夜班跨天情况
            is_overnight = False
            if shift_type == 'night_shift' and shift_end < shift_start:
                is_overnight = True
                self.logger.info(f"夜班是跨天班次: {shift_start} - {shift_end}")

            # 分配护理员到这个班次
            assigned_count = 0

            # 如果在轮班模式下，我们需要确保每个班次至少有一个人
            if is_rotation_mode and num_required > 0:
                # 在轮班模式下，我们需要找到最适合这个班次的护理员
                # 优先考虑还没有被分配班次的护理员
                available_caregivers = [c for c in sorted_caregivers if c.id not in assigned_caregivers]

                # 如果没有可用的护理员，则考虑已经被分配了一个班次的护理员
                if not available_caregivers:
                    available_caregivers = [c for c in sorted_caregivers if assigned_caregivers.get(c.id, 0) < 2]

                # 如果还是没有可用的护理员，则使用所有护理员
                if not available_caregivers:
                    available_caregivers = sorted_caregivers

                # 选择第一个可用的护理员
                if available_caregivers:
                    caregiver = available_caregivers[0]

                    # 更新已分配班次计数
                    assigned_caregivers[caregiver.id] = assigned_caregivers.get(caregiver.id, 0) + 1

                    # 获取中文班次名称
                    shift_name_cn = self.shift_config[shift_type]['name']

                    # 创建班次分配记录
                    assignment = ShiftAssignment(
                        id=str(uuid.uuid4()),
                        caregiver_id=caregiver.id,
                        caregiver_name=caregiver.attendant_name,
                        shift_date=target_date,
                        shift_type=shift_type,  # 使用完整的班次名称，而不是中文名称
                        shift_start_time=shift_start,
                        shift_end_time=shift_end,
                        schedule_type='daily'
                    )

                    shift_assignments.append(assignment)
                    assigned_count += 1
                    self.logger.info(f"轮班模式: 分配护理员 {caregiver.attendant_name} 到 {shift_name_cn}，时间 {shift_start}-{shift_end}{' (跨天)' if is_overnight else ''}")
            else:
                # 正常模式，为这个班次分配指定数量的护理员
                for _ in range(num_required):
                    # 找到还没有被分配班次的护理员
                    available_caregivers = [c for c in sorted_caregivers if c.id not in assigned_caregivers]

                    if available_caregivers:
                        caregiver = available_caregivers[0]

                        # 标记该护理员已分配班次
                        assigned_caregivers[caregiver.id] = 1

                        # 获取中文班次名称
                        shift_name_cn = self.shift_config[shift_type]['name']

                        # 创建班次分配记录
                        assignment = ShiftAssignment(
                            id=str(uuid.uuid4()),
                            caregiver_id=caregiver.id,
                            caregiver_name=caregiver.attendant_name,
                            shift_date=target_date,
                            shift_type=shift_type,  # 使用完整的班次名称，而不是中文名称
                            shift_start_time=shift_start,
                            shift_end_time=shift_end,
                            schedule_type='daily'
                        )

                        shift_assignments.append(assignment)
                        assigned_count += 1
                        self.logger.info(f"分配护理员 {caregiver.attendant_name} 到 {shift_name_cn}，时间 {shift_start}-{shift_end}{' (跨天)' if is_overnight else ''}")

            if assigned_count < num_required:
                self.logger.warning(f"{shift_name_cn} 实际分配了 {assigned_count} 个护理员，少于需求的 {num_required} 个")

        # 根据老人需求和护理员技能生成详细的时间段安排
        elder_schedules = {}
        time_slot_assignments = []

        # 为每个老人生成排班
        for elder in elders:
            elder_id = elder.elder

            # 不清除已有数据，而是每次生成新的数据
            # 记录日志，显示当前生成的是新数据
            self.logger.info(f"为老人 {elder_id} 在 {target_date} 生成新的排班计划")

            # 创建排班结果记录
            # 初始化为空列表，后面会添加时间段条目
            schedule_entries = []

            # 创建排班结果对象，使用当前时间作为时间戳
            current_datetime = datetime.now()

            # 初始化与原接口格式一致的时间段条目列表
            # 预先创建self.time_slots_per_day个时间段（默认为72个，每段20分钟），默认为睡眠活动
            initial_schedule_entries = []
            self.logger.info(f"为老人 {elder_id} 创建 {self.time_slots_per_day} 个时间段，每段 {self.time_interval_minutes} 分钟")

            # 调用封装的方法创建初始时间段
            initial_schedule_entries = self._create_initial_time_slots()

            # 创建新的排班结果对象，不检查是否已存在相同老人同一天的记录
            schedule_result_id = str(uuid.uuid4())
            self.logger.info(f"创建排班结果: {schedule_result_id}, 老人: {elder_id}, 日期: {current_datetime}")

            # 获取下一个批次号
            batch_number = self.get_next_batch_number()

            schedule_result = ScheduleResult(
                id=schedule_result_id,
                elder=elder_id,
                schedule_date=current_datetime,  # 使用当前时间，包含时分秒
                schedule_data=initial_schedule_entries,  # 初始化为默认的时间段条目列表
                plan_date=target_date,  # 使用目标日期作为排班日期
                batch_number=batch_number,  # 使用批次号
                create_time=current_datetime,  # 创建时间
                update_time=current_datetime,  # 更新时间
                status='active'  # 状态
            )

            # 记录日志
            self.logger.info(f"创建排班结果: {schedule_result.id}, 老人: {elder_id}, 日期: {schedule_result.schedule_date}")

            # 确保排班结果对象被添加到elder_schedules字典中
            elder_schedules[elder_id] = {
                'schedule_result': schedule_result,
                'time_slots': []
            }

            # 解析老人需要的护理动作
            elder_actions = elder.actions

            # 为每个时间段分配护理员和活动
            daily_slots = []
            # 创建与原接口格式一致的时间段条目列表
            schedule_entries = [None] * self.time_slots_per_day

            for time_slot in range(self.time_slots_per_day):
                # 计算实际时间
                hours = (time_slot * self.time_interval_minutes) // 60
                minutes = (time_slot * self.time_interval_minutes) % 60
                end_hours = ((time_slot + 1) * self.time_interval_minutes) // 60
                end_minutes = ((time_slot + 1) * self.time_interval_minutes) % 60

                # 处理跨天的情况
                is_overnight = False
                if end_hours >= 24:
                    is_overnight = True
                    display_end_hours = end_hours - 24
                else:
                    display_end_hours = end_hours

                time_str = f"{hours:02d}:{minutes:02d}"
                time_slot_str = f"{hours:02d}:{minutes:02d}-{display_end_hours:02d}:{end_minutes:02d}"

                # 记录日志
                log_message = f"时间段: {time_slot_str}"
                if is_overnight:
                    log_message += " (跨天)"
                self.logger.debug(log_message)

                # 找到当前时间段对应的班次和可用护理员
                current_shift = None
                current_time = self.parse_time(time_str)
                self.logger.info(f"检查时间段 {time_str} 对应的班次")

                # 只检查启用的班次
                for shift_type in active_shifts:
                    # 检查班次是否在当前班次配置中
                    if shift_type not in current_shift_config:
                        self.logger.warning(f"班次 {shift_type} 不在当前班次配置中，跳过")
                        continue

                    shift_start = self.parse_time(current_shift_config[shift_type]['start_time'])
                    shift_end = self.parse_time(current_shift_config[shift_type]['end_time'])

                    # 处理跨天情况
                    if shift_end < shift_start:  # 跨天班次
                        # 跨天班次：如果当前时间小于结束时间或大于等于开始时间，则在该班次内
                        if current_time < shift_end or current_time >= shift_start:
                            current_shift = shift_type
                            self.logger.info(f"时间 {time_str} 匹配{shift_type}(跨天): {shift_start}-{shift_end}")
                            break
                    else:
                        # 普通情况：如果当前时间在班次时间范围内，则匹配
                        if shift_start <= current_time < shift_end:
                            current_shift = shift_type
                            self.logger.info(f"时间 {time_str} 匹配{shift_type}: {shift_start}-{shift_end}")
                            break

                # 时间段字符串已经在前面计算好了，这里不需要重复计算

                if not current_shift:
                    self.logger.warning(f"时间 {time_str} 不在任何班次内，设置为睡眠活动")
                    # 如果不在任何班次内，创建一个睡眠活动的条目
                    entry = self.create_schedule_entry(
                        time_str=time_slot_str,
                        activity_name="睡眠",
                        activity_id="10",
                        caregiver_name="None",
                        caregiver_id="0"
                    )
                    schedule_entries[time_slot] = entry
                    continue

                # 找到当前班次的护理员
                # 使用完整的班次名称
                shift_caregivers = [a for a in shift_assignments if a.shift_type == current_shift]
                available_caregivers = []

                for assignment in shift_caregivers:
                    caregiver = next((c for c in caregivers if c.id == assignment.caregiver_id), None)
                    if caregiver and self.check_caregiver_availability(caregiver.id, target_date, time_slot):
                        available_caregivers.append(caregiver)

                # 根据老人需求匹配最合适的护理员
                matched_caregiver = self.match_caregiver_to_elder_needs(available_caregivers, elder_actions)

                # 如果没有匹配的护理员，但有可用的护理员，则随机选择一个
                if not matched_caregiver and available_caregivers:
                    matched_caregiver = random.choice(available_caregivers)
                    self.logger.info(f"在generate_care_timeline中随机选择护理员 {matched_caregiver.attendant_name}")
                # 如果没有可用的护理员，则从所有护理员中随机选择一个
                elif not matched_caregiver and caregivers:
                    matched_caregiver = random.choice(caregivers)
                    self.logger.warning(f"没有可用的护理员，随机选择一个护理员: {matched_caregiver.attendant_name}")

                # 如果有护理员（匹配的或随机选择的）
                if matched_caregiver:
                    # 从老人需求中选择一个护理动作
                    action_ids = self.parse_skills(elder_actions)
                    caregiver_skills = self.parse_skills(matched_caregiver.skills)

                    # 找到护理员能够执行的第一个护理动作
                    selected_action_id = next((action for action in action_ids if action in caregiver_skills), None)

                    # 如果没有匹配的动作，但有动作列表，则随机选择一个
                    if not selected_action_id and action_ids:
                        selected_action_id = random.choice(action_ids)
                        self.logger.warning(f"没有匹配的动作，随机选择一个: {selected_action_id}")

                    if selected_action_id:
                        # 获取护理动作详情
                        nursing_actions = self.get_nursing_actions()
                        action_name = nursing_actions.get(selected_action_id, DicNursingAction(name="未知活动")).name

                        # 创建时间段记录，确保包含完整的日期时间信息
                        current_time = datetime.now()  # 使用当前时间，包含年月日时分秒
                        time_slot_record = TimeSlot(
                            id=str(uuid.uuid4()),
                            schedule_result_id=schedule_result.id,
                            elder=elder.elder,
                            time_slot=time_slot_str,  # 使用完整的时间段格式
                            activity=action_name,
                            activity_id=str(selected_action_id),
                            caregiver=matched_caregiver.attendant_name,
                            caregiver_id=matched_caregiver.id,
                            time_order=time_slot,
                            schedule_date=current_time,  # 使用当前时间，包含年月日时分秒
                            plan_date=target_date,  # 使用目标日期作为排班日期
                            batch_number=schedule_result.batch_number,  # 使用与排班结果相同的批次号
                            create_time=current_time,  # 创建时间
                            update_time=current_time,  # 更新时间
                            status='active'  # 状态
                        )

                        # 记录日志
                        self.logger.debug(f"创建时间段记录: {time_slot_str}, 时间戳: {current_time}")

                        # 记录日志
                        self.logger.info(f"创建时间段: 老人 {elder.elder}, 护理员 {matched_caregiver.attendant_name}, 时间 {time_slot_str}, 活动 {action_name}")

                        # 添加到列表中
                        daily_slots.append(time_slot_record)
                        time_slot_assignments.append(time_slot_record)

                        # 添加到老人的时间段列表中
                        if elder.elder in elder_schedules:
                            elder_schedules[elder.elder]['time_slots'].append(time_slot_record)

                        # 更新护理员可用性
                        self.update_caregiver_availability(matched_caregiver.id, target_date, time_slot, False)

                        # 创建与原接口格式一致的时间段条目
                        entry = self.create_schedule_entry(
                            time_str=time_slot_str,
                            activity_name=action_name,
                            activity_id=str(selected_action_id),
                            caregiver_name=matched_caregiver.attendant_name,
                            caregiver_id=matched_caregiver.id
                        )
                    else:
                        # 如果没有选择的动作，创建一个睡眠活动的条目
                        entry = self.create_schedule_entry(
                            time_str=time_slot_str,
                            activity_name="睡眠",
                            activity_id="10",
                            caregiver_name="None",
                            caregiver_id="0"
                        )
                else:
                    # 如果没有匹配的护理员，创建一个睡眠活动的条目
                    entry = self.create_schedule_entry(
                        time_str=time_slot_str,
                        activity_name="睡眠",
                        activity_id="10",
                        caregiver_name="None",
                        caregiver_id="0"
                    )

                # 添加到时间段条目列表中
                schedule_entries[time_slot] = entry

            # 确保所有时间段都有条目，将None替换为睡眠活动
            for time_slot in range(self.time_slots_per_day):
                if schedule_entries[time_slot] is None:
                    # 计算实际时间
                    hours = (time_slot * self.time_interval_minutes) // 60
                    minutes = (time_slot * self.time_interval_minutes) % 60

                    # 计算结束时间
                    end_minutes = minutes + self.time_interval_minutes
                    end_hours = hours
                    if end_minutes >= 60:
                        end_minutes -= 60
                        end_hours += 1
                        if end_hours >= 24:
                            end_hours -= 24

                    # 格式化时间段字符串
                    time_slot_str = f"{hours:02d}:{minutes:02d}-{end_hours:02d}:{end_minutes:02d}"

                    # 创建睡眠活动的条目
                    schedule_entries[time_slot] = self.create_schedule_entry(
                        time_str=time_slot_str,
                        activity_name="睡眠",
                        activity_id="10",
                        caregiver_name="None",
                        caregiver_id="0"
                    )

            # 确保所有时间段都有条目，将None替换为睡眠活动
            for time_slot in range(self.time_slots_per_day):
                if schedule_entries[time_slot] is None:
                    # 计算实际时间
                    hours = (time_slot * self.time_interval_minutes) // 60
                    minutes = (time_slot * self.time_interval_minutes) % 60
                    end_hours = ((time_slot + 1) * self.time_interval_minutes) // 60
                    end_minutes = ((time_slot + 1) * self.time_interval_minutes) % 60

                    # 处理跨天的情况
                    is_overnight = False
                    if end_hours >= 24:
                        is_overnight = True
                        display_end_hours = end_hours - 24
                    else:
                        display_end_hours = end_hours

                    # 格式化时间段字符串
                    time_slot_str = f"{hours:02d}:{minutes:02d}-{display_end_hours:02d}:{end_minutes:02d}"

                    # 记录日志
                    log_message = f"时间段: {time_slot_str}"
                    if is_overnight:
                        log_message += " (跨天)"
                    self.logger.debug(log_message)

                    # 创建睡眠活动的条目
                    schedule_entries[time_slot] = self.create_schedule_entry(
                        time_str=time_slot_str,
                        activity_name="睡眠",
                        activity_id="10",
                        caregiver_name="None",
                        caregiver_id="0"
                    )

            # 转换为列表并按时间排序
            schedule_entries = [entry for entry in schedule_entries if entry is not None]

            # 按时间排序
            schedule_entries.sort(key=lambda x: x['TimeSlot'])

            # 更新排班结果的JSON数据
            if elder.elder in elder_schedules:
                schedule_result = elder_schedules[elder.elder]['schedule_result']

                # 使用辅助方法验证和修复排班数据
                schedule_entries = self._validate_and_fix_schedule_data(schedule_entries)

                # 更新排班结果的数据
                schedule_result.schedule_data = schedule_entries

                # 记录日志
                self.logger.info(f"更新老人 {elder.elder} 的排班结果，包含 {len(schedule_entries)} 个时间段条目")

                # 确保时间段列表已经添加到elder_schedules字典中
                self.logger.info(f"老人 {elder.elder} 的排班结果包含 {len(elder_schedules[elder.elder]['time_slots'])} 个时间段和 {len(schedule_entries)} 个时间段条目")

        return {
            'shift_assignments': shift_assignments,
            'elder_schedules': elder_schedules,
            'time_slot_assignments': time_slot_assignments
        }

    def generate_schedule(self, schedule_type='daily', num_days=1, start_date=None, shift_start_time=None):
        """生成排班计划

        参数:
            schedule_type: 排班类型（'daily', 'weekly', 'monthly', 'quarterly', 'yearly'）
            num_days: 当选择daily时，指定天数
            start_date: 开始日期，默认为今天
            shift_start_time: 班次开始时间，格式为'HH:MM'
        """
        if not start_date:
            start_date = date.today()
        elif isinstance(start_date, str):
            start_date = datetime.strptime(start_date, "%Y-%m-%d").date()

        # 根据排班类型确定结束日期
        if schedule_type == 'daily':
            end_date = start_date + timedelta(days=num_days - 1)
        elif schedule_type == 'weekly':
            end_date = start_date + timedelta(days=6)  # 一周7天
        elif schedule_type == 'monthly':
            # 计算下个月的同一天
            if start_date.month == 12:
                end_date = date(start_date.year + 1, 1, start_date.day)
            else:
                # 处理月末日期问题
                next_month = start_date.month + 1
                last_day = 31  # 默认最大天数
                while True:
                    try:
                        end_date = date(start_date.year, next_month, last_day)
                        break
                    except ValueError:
                        last_day -= 1
        elif schedule_type == 'quarterly':
            # 计算三个月后的同一天
            month = start_date.month + 3
            year = start_date.year
            if month > 12:
                month -= 12
                year += 1
            # 处理月末日期问题
            last_day = 31  # 默认最大天数
            while True:
                try:
                    end_date = date(year, month, last_day)
                    break
                except ValueError:
                    last_day -= 1
        elif schedule_type == 'yearly':
            end_date = date(start_date.year + 1, start_date.month, start_date.day) - timedelta(days=1)
        else:
            raise ValueError(f"不支持的排班类型: {schedule_type}")

        # 获取所有护理员和需要安排的老人
        all_caregivers = self.get_caregivers()
        all_elders = self.get_elders()

        if not all_caregivers:
            raise ValueError("没有可用的护理员")

        if not all_elders:
            raise ValueError("没有需要安排的老人")

        # 生成每一天的排班
        all_results = {
            'shift_assignments': [],  # 班次分配
            'schedule_results': [],   # 排班结果
            'time_slots': [],         # 时间段分配
            'elder_schedules': {}     # 老人排班计划，用于中间处理
        }

        current_date = start_date

        # 初始化或获取护理员排班次数跟踪器
        if not hasattr(self, '_caregiver_assignment_count'):
            self._caregiver_assignment_count = {caregiver.id: 0 for caregiver in all_caregivers}
            self.logger.info(f"初始化护理员排班计数: {self._caregiver_assignment_count}")

        # 跟踪每日排班的护理员，实现轮班制
        while current_date <= end_date:
            self.logger.info(f"开始生成 {current_date} 的排班计划")
            self.logger.info(f"当前护理员排班次数: {self._caregiver_assignment_count}")

            # 生成当天的排班
            daily_results = self.generate_daily_schedule(
                current_date,
                all_caregivers,  # 传入所有护理员，在方法内部会根据排班次数排序
                all_elders,
                shift_start_time
            )

            # 更新护理员排班次数
            for assignment in daily_results['shift_assignments']:
                caregiver_id = assignment.caregiver_id
                self._caregiver_assignment_count[caregiver_id] = self._caregiver_assignment_count.get(caregiver_id, 0) + 1
                self.logger.info(f"护理员 {assignment.caregiver_name} 排班次数增加到 {self._caregiver_assignment_count[caregiver_id]}")

            # 合并结果
            all_results['shift_assignments'].extend(daily_results['shift_assignments'])

            # 添加老人排班结果
            for elder_id, elder_schedule in daily_results['elder_schedules'].items():
                # 添加排班结果和时间段
                all_results['schedule_results'].append(elder_schedule['schedule_result'])
                all_results['time_slots'].extend(elder_schedule['time_slots'])

                # 将老人排班计划存入elder_schedules字典，以便后续处理
                if elder_id not in all_results['elder_schedules']:
                    all_results['elder_schedules'][elder_id] = {
                        'schedule_results': [],
                        'time_slots': []
                    }

                all_results['elder_schedules'][elder_id]['schedule_results'].append(elder_schedule['schedule_result'])
                all_results['elder_schedules'][elder_id]['time_slots'].extend(elder_schedule['time_slots'])

            current_date += timedelta(days=1)

        # 输出最终的护理员排班统计
        self.logger.info(f"最终护理员排班统计:")
        for caregiver in all_caregivers:
            self.logger.info(f"护理员 {caregiver.attendant_name} (编号: {caregiver.id}) 排班次数: {self._caregiver_assignment_count[caregiver.id]}")

        # 分析排班结果
        shift_counts = {'班次1': 0, '班次2': 0, '班次3': 0}
        for assignment in all_results['shift_assignments']:
            if assignment.shift_type in shift_counts:
                shift_counts[assignment.shift_type] += 1

        self.logger.info(f"排班统计: 班次1 {shift_counts['班次1']} 人次, 班次2 {shift_counts['班次2']} 人次, 班次3 {shift_counts['班次3']} 人次")

        return all_results

    def get_next_batch_number(self):
        """获取下一个批次号

        Returns:
            int: 下一个批次号
        """
        try:
            # 查询当前最大的批次号
            from sqlalchemy import func
            max_batch = db.session.query(func.max(ScheduleResult.batch_number)).scalar()

            # 如果没有记录，返回1，否则返回最大批次号+1
            if max_batch is None:
                return 1
            else:
                return max_batch + 1
        except Exception as e:
            self.logger.error(f"获取下一个批次号时出错: {str(e)}")
            # 如果出错，返回当前时间戳作为批次号
            import time
            return int(time.time())

    def save_shift_assignments(self, shift_assignments):
        """仅保存班次分配到数据库

        Args:
            shift_assignments: 班次分配列表

        Returns:
            bool: 是否保存成功
        """
        try:
            # 开始事务
            db.session.begin()

            # 保存班次分配到shift_assignments表
            for assignment in shift_assignments:
                db.session.add(assignment)

            # 提交事务
            db.session.commit()

            # 记录保存的数据
            self.logger.info(f"成功保存排班计划:")
            self.logger.info(f"  - {len(shift_assignments)} 个班次分配到shift_assignments表")
            self.logger.info("重要：护理员可用性数据仅在内存中跟踪，不写入caregiver_schedules表")
            return True
        except Exception as e:
            db.session.rollback()
            self.logger.error(f"保存班次分配失败: {str(e)}")
            return False

    # 提供一个安全的事务上下文
    @staticmethod
    @contextmanager
    def safe_transaction(session):
        try:
            yield
            session.commit()
        except Exception:
            session.rollback()
            raise

    def create_schedule_entry(self, time_str, activity_name, activity_id, caregiver_name, caregiver_id):
        """创建与原接口格式一致的时间段条目

        Args:
            time_str: 时间段字符串，格式为"HH:MM-HH:MM"
            activity_name: 活动名称
            activity_id: 活动ID
            caregiver_name: 护理员名称
            caregiver_id: 护理员ID

        Returns:
            时间段条目字典
        """
        return {
            "TimeSlot": time_str,
            "Activity": activity_name,
            "ActivityId": activity_id,
            "Caregiver": caregiver_name,
            "CaregiverId": caregiver_id
        }

    def save_schedule(self, schedule_results, target_date=None):
        """保存排班计划到数据库

        保存数据到以下表：
        - shift_assignments表：班次分配
        - schedule_results表：排班结果
        - time_slots表：时间段分配

        Args:
            schedule_results: 排班结果字典
            target_date: 目标日期，如果为None，则使用当前日期

        注意：不会向caregiver_schedules表写入数据
        """
        try:
            # 处理target_date参数
            if target_date is None:
                # 如果没有提供目标日期，使用当前日期
                from datetime import date
                target_date = date.today()
            elif isinstance(target_date, str):
                # 如果目标日期是字符串，转换为日期对象
                from datetime import datetime
                target_date = datetime.strptime(target_date, '%Y-%m-%d').date()

            self.logger.info(f"目标日期: {target_date}")

            # 使用安全的事务上下文
            with self.safe_transaction(db.session):
                # 1. 保存班次分配到shift_assignments表
                if schedule_results.get('shift_assignments'):
                    self.logger.info(f"批量保存 {len(schedule_results['shift_assignments'])} 个班次分配")
                    db.session.bulk_save_objects(schedule_results['shift_assignments'])

                # 2. 保存排班结果到schedule_results表
                schedule_results_count = 0
                self.logger.info(f"elder_schedules字典中有 {len(schedule_results.get('elder_schedules', {}))} 个老人")
                for elder_name, elder_schedule in schedule_results.get('elder_schedules', {}).items():
                    self.logger.info(f"处理老人 {elder_name} 的排班结果")

                    # 处理单个排班结果对象
                    if 'schedule_result' in elder_schedule:
                        schedule_result = elder_schedule.get('schedule_result')
                        if schedule_result:
                            # 确保 schedule_data 是有效的数据
                            schedule_result.schedule_data = self._validate_and_fix_schedule_data(schedule_result.schedule_data)

                            # 转换为JSON字符串
                            schedule_result.schedule_data = json.dumps(schedule_result.schedule_data)

                            self.logger.info(f"添加排班结果: {schedule_result.id}, 老人: {schedule_result.elder}")
                            db.session.add(schedule_result)
                            schedule_results_count += 1
                        else:
                            self.logger.warning(f"老人 {elder_name} 的排班结果为None")
                            # 创建新的排班结果
                            import uuid
                            from datetime import datetime

                            # 创建与原接口格式一致的时间段条目列表
                            schedule_entries = [None] * self.time_slots_per_day
                            time_slots = elder_schedule.get('time_slots', [])

                            # 创建时间段字典，用于去重
                            time_slot_dict = {}

                            # 如果有时间段列表，则创建时间段条目
                            if time_slots:
                                for slot in time_slots:
                                    # 解析时间字符串，获取小时和分钟
                                    hours, minutes = map(int, slot.time_slot.split(':'))
                                    # 计算时间段索引，根据时间间隔动态计算
                                    time_index = hours * (60 // self.time_interval_minutes) + (minutes // self.time_interval_minutes)

                                    # 确保索引在有效范围内
                                    if 0 <= time_index < self.time_slots_per_day:
                                        # 如果该时间段已经有条目，跳过
                                        if time_index in time_slot_dict:
                                            continue

                                        entry = self.create_schedule_entry(
                                            time_str=slot.time_slot,
                                            activity_name=slot.activity,
                                            activity_id=slot.activity_id,
                                            caregiver_name=slot.caregiver,
                                            caregiver_id=slot.caregiver_id
                                        )
                                        schedule_entries[time_index] = entry
                                        time_slot_dict[time_index] = True

                            # 确保生成完整的48个时间段
                            # 填充空缺的时间段
                            for time_slot in range(self.time_slots_per_day):
                                # 如果这个时间段还没有条目，创建一个睡眠活动的条目
                                if schedule_entries[time_slot] is None:
                                    # 计算实际时间
                                    hours = (time_slot * self.time_interval_minutes) // 60
                                    minutes = (time_slot * self.time_interval_minutes) % 60
                                    end_hours = ((time_slot + 1) * self.time_interval_minutes) // 60
                                    end_minutes = ((time_slot + 1) * self.time_interval_minutes) % 60

                                    # 处理跨天的情况
                                    is_overnight = False
                                    if end_hours >= 24:
                                        is_overnight = True
                                        display_end_hours = end_hours - 24
                                    else:
                                        display_end_hours = end_hours

                                    time_str = f"{hours:02d}:{minutes:02d}-{display_end_hours:02d}:{end_minutes:02d}"

                                    # 记录日志
                                    log_message = f"时间段: {time_str}"
                                    if is_overnight:
                                        log_message += " (跨天)"
                                    self.logger.debug(log_message)

                                    entry = self.create_schedule_entry(
                                        time_str=time_str,
                                        activity_name="睡眠",
                                        activity_id="10",
                                        caregiver_name="None",
                                        caregiver_id="0"
                                    )
                                    schedule_entries[time_slot] = entry

                            # 按时间排序
                            schedule_entries.sort(key=lambda x: x['TimeSlot'])

                            # 创建排班结果对象，使用列表格式的schedule_data
                            # 确保每个时间段条目都有正确的格式
                            for entry in schedule_entries:
                                if not isinstance(entry, dict):
                                    continue
                                # 确保必要的字段存在
                                if 'TimeSlot' not in entry:
                                    entry['TimeSlot'] = "00:00-00:30"
                                if 'Activity' not in entry:
                                    entry['Activity'] = "睡眠"
                                if 'ActivityId' not in entry:
                                    entry['ActivityId'] = "10"
                                if 'Caregiver' not in entry:
                                    entry['Caregiver'] = "None"
                                if 'CaregiverId' not in entry:
                                    entry['CaregiverId'] = "0"

                            # 检查是否已存在相同老人同一天的记录
                            today = datetime.now().date()
                            existing_result = ScheduleResult.query.filter(
                                ScheduleResult.elder == elder_name,
                                func.date(ScheduleResult.schedule_date) == today
                            ).order_by(ScheduleResult.schedule_date.desc()).first()

                            if existing_result:
                                # 如果已存在记录，更新而不是创建新记录
                                self.logger.info(f"发现老人 {elder_name} 已有今天的排班结果，进行更新")
                                existing_result.schedule_date = datetime.now()  # 更新时间戳
                                existing_result.schedule_data = json.dumps(schedule_entries)  # 更新数据
                                schedule_results_count += 1

                                # 更新elder_schedule中的schedule_result
                                elder_schedule['schedule_result'] = existing_result
                            else:
                                # 如果不存在，创建新的排班结果对象
                                new_schedule_result = ScheduleResult(
                                    id=str(uuid.uuid4()),
                                    elder=elder_name,
                                    schedule_date=datetime.now(),  # 使用当前时间，包含时分秒
                                    schedule_data=json.dumps(schedule_entries)  # 转换为JSON字符串
                                )

                                self.logger.info(f"创建新的排班结果: {new_schedule_result.id}, 老人: {new_schedule_result.elder}")
                                db.session.add(new_schedule_result)
                                schedule_results_count += 1

                                # 更新elder_schedule中的schedule_result
                                elder_schedule['schedule_result'] = new_schedule_result

                    # 处理排班结果列表
                    elif 'schedule_results' in elder_schedule:
                        schedule_results_list = elder_schedule.get('schedule_results', [])
                        self.logger.info(f"老人 {elder_name} 有 {len(schedule_results_list)} 个排班结果")

                        for schedule_result in schedule_results_list:
                            if schedule_result:
                                # 确保 schedule_data 是有效的数据
                                schedule_result.schedule_data = self._validate_and_fix_schedule_data(schedule_result.schedule_data)

                                # 转换为JSON字符串
                                schedule_result.schedule_data = json.dumps(schedule_result.schedule_data)

                                self.logger.info(f"添加排班结果: {schedule_result.id}, 老人: {schedule_result.elder}")
                                db.session.add(schedule_result)
                                schedule_results_count += 1
                            else:
                                self.logger.warning(f"老人 {elder_name} 的排班结果列表中有None元素")

                    # 既没有单个排班结果也没有排班结果列表
                    else:
                        self.logger.warning(f"老人 {elder_name} 的排班结果不存在")
                        # 创建新的排班结果
                        import uuid
                        from datetime import datetime

                        # 创建新的排班结果，使用与原接口一致的格式
                        # 首先检查是否有时间段列表
                        time_slots = elder_schedule.get('time_slots', [])

                        # 创建与原接口格式一致的时间段条目列表
                        schedule_entries = [None] * self.time_slots_per_day

                        # 创建时间段字典，用于去重
                        time_slot_dict = {}

                        # 如果有时间段列表，则创建时间段条目
                        if time_slots:
                            for slot in time_slots:
                                # 解析时间字符串，获取小时和分钟
                                hours, minutes = map(int, slot.time_slot.split(':'))
                                # 计算时间段索引，根据时间间隔动态计算
                                time_index = hours * (60 // self.time_interval_minutes) + (minutes // self.time_interval_minutes)

                                # 确保索引在有效范围内
                                if 0 <= time_index < self.time_slots_per_day:
                                    # 如果该时间段已经有条目，跳过
                                    if time_index in time_slot_dict:
                                        continue

                                    entry = self.create_schedule_entry(
                                        time_str=slot.time_slot,
                                        activity_name=slot.activity,
                                        activity_id=slot.activity_id,
                                        caregiver_name=slot.caregiver,
                                        caregiver_id=slot.caregiver_id
                                    )
                                    schedule_entries[time_index] = entry
                                    time_slot_dict[time_index] = True

                        # 确保生成完整的时间段，数量由self.time_slots_per_day决定
                        # 填充空缺的时间段
                        for time_slot in range(self.time_slots_per_day):
                            # 如果这个时间段还没有条目，创建一个睡眠活动的条目
                            if schedule_entries[time_slot] is None:
                                # 计算实际时间
                                hours = (time_slot * self.time_interval_minutes) // 60
                                minutes = (time_slot * self.time_interval_minutes) % 60
                                time_str = f"{hours:02d}:{minutes:02d}"

                                entry = self.create_schedule_entry(
                                    time_str=time_str,
                                    activity_name="睡眠",
                                    activity_id="10",
                                    caregiver_name="None",
                                    caregiver_id="0"
                                )
                                schedule_entries[time_slot] = entry

                        # 按时间排序
                        schedule_entries.sort(key=lambda x: x['TimeSlot'])

                        # 创建排班结果对象
                        # 确保每个时间段条目都有正确的格式
                        for entry in schedule_entries:
                            if not isinstance(entry, dict):
                                continue
                            # 确保必要的字段存在
                            if 'TimeSlot' not in entry:
                                entry['TimeSlot'] = "00:00-00:30"
                            if 'Activity' not in entry:
                                entry['Activity'] = "睡眠"
                            if 'ActivityId' not in entry:
                                entry['ActivityId'] = "10"
                            if 'Caregiver' not in entry:
                                entry['Caregiver'] = "None"
                            if 'CaregiverId' not in entry:
                                entry['CaregiverId'] = "0"

                        # 创建新的排班结果对象，不检查是否已存在相同老人同一天的记录
                        # 获取下一个批次号
                        batch_number = self.get_next_batch_number()
                        current_datetime = datetime.now()

                        # 解析目标日期
                        if isinstance(target_date, str):
                            from datetime import datetime
                            plan_date = datetime.strptime(target_date, '%Y-%m-%d').date()
                        else:
                            plan_date = target_date

                        new_schedule_result = ScheduleResult(
                            id=str(uuid.uuid4()),
                            elder=elder_name,
                            schedule_date=current_datetime,  # 使用当前时间，包含时分秒
                            schedule_data=json.dumps(schedule_entries),  # 转换为JSON字符串
                            plan_date=plan_date,  # 使用目标日期作为排班日期
                            batch_number=batch_number,  # 使用批次号
                            create_time=current_datetime,  # 创建时间
                            update_time=current_datetime,  # 更新时间
                            status='active'  # 状态
                        )

                        self.logger.info(f"创建新的排班结果: {new_schedule_result.id}, 老人: {new_schedule_result.elder}")
                        db.session.add(new_schedule_result)
                        schedule_results_count += 1

                        # 更新elder_schedule中的schedule_result
                        elder_schedule['schedule_result'] = new_schedule_result

                # 3. 保存时间段分配到time_slots表
                time_slots_count = 0
                time_slots_to_add = []  # 收集所有要添加的时间段，减少数据库查询次数

                for elder_name, elder_schedule in schedule_results.get('elder_schedules', {}).items():
                    time_slots = elder_schedule.get('time_slots', [])
                    schedule_result = elder_schedule.get('schedule_result')

                    self.logger.info(f"处理 {len(time_slots)} 个时间段记录")

                    # 确保时间段与排班结果正确关联
                    for time_slot in time_slots:
                        try:
                            # 数据验证和清理
                            if not time_slot.time_slot or not isinstance(time_slot.time_slot, str):
                                self.logger.warning(f"时间段无效: {time_slot.time_slot}")
                                continue

                            # 确保时间段格式正确
                            if ':' not in time_slot.time_slot:
                                self.logger.warning(f"时间段格式无效: {time_slot.time_slot}")
                                continue

                            # 确保活动信息存在
                            if not time_slot.activity:
                                time_slot.activity = "睡眠"

                            # 确保活动ID存在
                            if not time_slot.activity_id:
                                time_slot.activity_id = "10"

                            # 确保护理员信息存在
                            if not time_slot.caregiver:
                                time_slot.caregiver = "None"

                            # 确保护理员ID存在
                            if not time_slot.caregiver_id:
                                time_slot.caregiver_id = "0"

                            # 确保时间戳存在
                            from datetime import datetime
                            current_time = datetime.now()
                            if not time_slot.schedule_date:
                                time_slot.schedule_date = current_time

                            # 添加新字段
                            if not hasattr(time_slot, 'plan_date') or not time_slot.plan_date:
                                time_slot.plan_date = target_date

                            if not hasattr(time_slot, 'batch_number') or not time_slot.batch_number:
                                # 如果有排班结果，使用相同的批次号
                                if schedule_result and hasattr(schedule_result, 'batch_number'):
                                    time_slot.batch_number = schedule_result.batch_number
                                else:
                                    # 否则获取一个新的批次号
                                    time_slot.batch_number = self.get_next_batch_number()

                            if not hasattr(time_slot, 'create_time') or not time_slot.create_time:
                                time_slot.create_time = current_time

                            if not hasattr(time_slot, 'update_time') or not time_slot.update_time:
                                time_slot.update_time = current_time

                            if not hasattr(time_slot, 'status') or not time_slot.status:
                                time_slot.status = 'active'

                            # 关联到排班结果
                            if schedule_result and not time_slot.schedule_result_id:
                                time_slot.schedule_result_id = schedule_result.id
                                self.logger.debug(f"关联时间段到排班结果: {schedule_result.id}")

                            # 添加到待保存列表
                            time_slots_to_add.append(time_slot)
                            time_slots_count += 1

                            # 每500条数据批量保存一次，减少内存占用
                            if len(time_slots_to_add) >= 500:
                                self.logger.info(f"批量保存 {len(time_slots_to_add)} 个时间段记录")
                                db.session.bulk_save_objects(time_slots_to_add)
                                time_slots_to_add = []
                        except Exception as e:
                            self.logger.error(f"处理时间段时出错: {str(e)}")
                            continue

                # 保存剩余的时间段记录
                if time_slots_to_add:
                    self.logger.info(f"批量保存剩余的 {len(time_slots_to_add)} 个时间段记录")
                    db.session.bulk_save_objects(time_slots_to_add)

                # 提交事务由safe_transaction自动处理

            # 记录保存的数据
            self.logger.info(f"成功保存排班计划:")
            self.logger.info(f"  - {len(schedule_results['shift_assignments'])} 个班次分配到shift_assignments表")
            self.logger.info(f"  - {schedule_results_count} 个排班结果到schedule_results表")
            self.logger.info(f"  - {time_slots_count} 个时间段分配到time_slots表")
            self.logger.info("重要：护理员可用性数据仅在内存中跟踪，不写入caregiver_schedules表")
            return True
        except Exception as e:
            db.session.rollback()
            self.logger.error(f"保存排班计划失败: {str(e)}")
            return False

    def generate_and_save_schedule(self, schedule_type=None, num_days=None, start_date=None, shift_start_time=None, generate_care_timeline=None, shift_mode=None, custom_shifts=None, time_slots_per_day=None, time_interval_minutes=None):
        """生成并保存排班计划

        Args:
            schedule_type: 排班类型，1(按天), 2(按周), 3(按月), 4(按季度), 5(按年)
            num_days: 排班天数，当schedule_type为1(按天)时有效
            start_date: 开始日期，如果为None则使用全局参数或当前日期
            shift_start_time: 班次开始时间
            generate_care_timeline: 是否生成照护时间线，如果为None，则使用全局参数
            shift_mode: 班制模式，如'one_shift', 'two_shift', 'three_shift', 'custom'
            custom_shifts: 自定义班制模式下的班次列表
            time_slots_per_day: 一天中的时间槽数量，如果为None则使用全局参数
            time_interval_minutes: 每个时间槽的分钟数，如果为None则使用全局参数
        """
        from config_params import three_shift_params

        # 使用全局参数作为默认值
        if schedule_type is None:
            schedule_type = three_shift_params.get('schedule_type', 1)

        if num_days is None:
            num_days = three_shift_params.get('num_days', 1)

        if start_date is None:
            start_date = three_shift_params.get('start_date')

        if shift_start_time is None:
            shift_start_time = three_shift_params.get('shift_start_time', '06:00')

        if generate_care_timeline is None:
            generate_care_timeline = three_shift_params.get('auto_schedule_generate_care_timeline', True)

        # 处理时间槽参数
        original_time_slots_per_day = self.time_slots_per_day
        original_time_interval_minutes = self.time_interval_minutes

        if time_slots_per_day is not None:
            try:
                self.time_slots_per_day = int(time_slots_per_day)
                self.logger.info(f"设置时间槽数量为: {self.time_slots_per_day}")
            except (ValueError, TypeError):
                self.logger.warning(f"无效的时间槽数量: {time_slots_per_day}，使用默认值: {self.time_slots_per_day}")

        if time_interval_minutes is not None:
            try:
                self.time_interval_minutes = int(time_interval_minutes)
                self.logger.info(f"设置时间间隔分钟数为: {self.time_interval_minutes}")
            except (ValueError, TypeError):
                self.logger.warning(f"无效的时间间隔分钟数: {time_interval_minutes}，使用默认值: {self.time_interval_minutes}")

        # 检查时间槽参数是否有效
        if self.time_slots_per_day * self.time_interval_minutes != 24 * 60:
            self.logger.warning(f"时间槽参数无效: {self.time_slots_per_day} 个时间槽，每个 {self.time_interval_minutes} 分钟，总时间为 {self.time_slots_per_day * self.time_interval_minutes} 分钟，不等于24小时(1440分钟)")
            self.logger.info(f"恢复为原始参数: {original_time_slots_per_day} 个时间槽，每个 {original_time_interval_minutes} 分钟")
            self.time_slots_per_day = original_time_slots_per_day
            self.time_interval_minutes = original_time_interval_minutes

        # 处理班制模式参数
        if shift_mode is None:
            # 使用全局参数中的shift_mode
            shift_mode = three_shift_params.get('shift_mode', 3)
            self.logger.info(f"使用全局参数中的班制模式: {shift_mode}")

        # 如果是数字，转换为对应的班制模式字符串
        if isinstance(shift_mode, int) or (isinstance(shift_mode, str) and shift_mode.isdigit()):
            shift_number = int(shift_mode)
            if shift_number == 1:
                shift_mode = 'one_shift'
                self.logger.info(f"数字班制参数 {shift_number} 映射到一班制")
            elif shift_number == 2:
                shift_mode = 'two_shift'
                self.logger.info(f"数字班制参数 {shift_number} 映射到二班制")
            elif shift_number == 3:
                shift_mode = 'three_shift'
                self.logger.info(f"数字班制参数 {shift_number} 映射到三班制")
            elif shift_number == 4:
                shift_mode = 'custom'
                self.logger.info(f"数字班制参数 {shift_number} 映射到自定义班制")
                # 如果没有提供自定义班次，使用全局参数中的custom_shifts
                if custom_shifts is None:
                    custom_shifts = three_shift_params.get('custom_shifts')
            else:
                self.logger.warning(f"无效的数字班制参数: {shift_number}，使用默认的三班制")
                shift_mode = 'three_shift'

        # 检查班制模式是否有效
        valid_modes = ['one_shift', 'two_shift', 'three_shift', 'multi_shift', 'custom']
        if shift_mode not in valid_modes:
            self.logger.warning(f"无效的班制模式: {shift_mode}，使用默认的三班制")
            shift_mode = 'three_shift'

        self.logger.info(f"设置班制模式: {shift_mode}")

        # 如果是自定义模式，需要检查是否提供了自定义班次
        if shift_mode == 'custom':
            # 只有在自定义模式下才处理custom_shifts参数
            if custom_shifts is None:
                # 尝试从全局参数中获取自定义班次
                global_custom_shifts = three_shift_params.get('custom_shifts')
                if global_custom_shifts:
                    self.logger.info(f"使用全局参数中的自定义班次: {global_custom_shifts}")
                    custom_shifts = global_custom_shifts
                else:
                    self.logger.warning(f"自定义模式下没有提供班次列表，使用默认的三班制")
                    shift_mode = 'three_shift'
            else:
                self.logger.info(f"自定义模式，使用自定义班次: {custom_shifts}")
        else:
            # 非自定义模式下，忽略custom_shifts参数
            if custom_shifts is not None:
                self.logger.warning(f"在非自定义模式({shift_mode})下提供了custom_shifts参数({custom_shifts})，将被忽略")
                custom_shifts = None

        # 设置班制模式
        if shift_mode is not None or custom_shifts is not None:
            self.set_shift_mode(shift_mode, custom_shifts, shift_start_time)
        elif shift_start_time is not None:
            # 如果只指定了开始时间，使用当前班制模式并更新开始时间
            self.logger.info(f"使用当前班制模式 {self.active_mode}，更新班次开始时间: {shift_start_time}")
            self.set_shift_mode(self.active_mode, None, shift_start_time)
        else:
            # 如果没有指定班制模式和开始时间，使用默认的三班制
            default_mode = 'three_shift'  # 默认使用三班制
            self.logger.info(f"未指定班制模式和开始时间，使用默认班制模式: {default_mode}")
            self.set_shift_mode(default_mode, None, None)

        # 将数字排班类型转换为字符串类型（与原有代码兼容）
        schedule_type_map = {
            1: 'daily',
            2: 'weekly',
            3: 'monthly',
            4: 'quarterly',
            5: 'yearly'
        }

        # 如果是数字类型，转换为字符串
        if isinstance(schedule_type, int):
            schedule_type_str = schedule_type_map.get(schedule_type)
            if schedule_type_str is None:
                self.logger.warning(f"不支持的排班类型数字: {schedule_type}, 使用默认值'daily'")
                schedule_type_str = 'daily'
        else:
            # 如果已经是字符串，直接使用
            schedule_type_str = schedule_type

        self.logger.info(f"开始生成排班计划: 类型={schedule_type}({schedule_type_str}), 天数={num_days}, 开始日期={start_date}, 生成照护时间线={generate_care_timeline}")

        # 确保参数类型正确
        try:
            if isinstance(num_days, str):
                num_days = int(num_days)
                self.logger.info(f"将天数从字符串转换为整数: {num_days}")
        except ValueError as e:
            self.logger.warning(f"天数转换失败, 使用默认值1: {str(e)}")
            num_days = 1

        # 生成排班计划
        start_time = datetime.now()
        self.logger.info(f"开始生成排班计划...")
        schedule_results = self.generate_schedule(schedule_type_str, num_days, start_date, shift_start_time)
        end_time = datetime.now()
        generation_time = (end_time - start_time).total_seconds()
        self.logger.info(f"排班计划生成完成, 耗时 {generation_time:.2f} 秒")

        # 如果启用了算法优化，使用算法优化排班方案
        # 默认启用算法优化，除非显式禁用
        use_optimization = params.get('use_algorithm_optimization', True)
        algorithm_type = params.get('algorithm_type', 'hybrid')

        # 记录算法参数
        self.logger.info(f"算法参数: algorithm_type={algorithm_type}, population_size={self.population_size}, "
                         f"crossover_rate={self.crossover_rate}, mutation_rate={self.mutation_rate}, "
                         f"phenotype_limit={self.phenotype_limit}, initial_temperature={self.initial_temperature}, "
                         f"cooling_rate={self.cooling_rate}")

        if use_optimization:
            self.logger.info(f"开始使用算法优化排班方案... 算法类型: {algorithm_type}")
            start_time = datetime.now()
            all_caregivers = self.get_caregivers()

            # 根据算法类型选择不同的算法
            if algorithm_type == 'ga':
                # 仅使用遗传算法
                self.logger.info("使用遗传算法优化排班方案")
                schedule_results = self.genetic_algorithm_optimize(schedule_results, all_caregivers)
            elif algorithm_type == 'sa':
                # 仅使用模拟退火算法
                self.logger.info("使用模拟退火算法优化排班方案")
                schedule_results = self.simulated_annealing_optimize(schedule_results, all_caregivers)
            elif algorithm_type == 'nsga2' and 'ADVANCED_ALGORITHMS_AVAILABLE' in globals() and ADVANCED_ALGORITHMS_AVAILABLE:
                # 使用NSGA-II多目标优化算法
                self.logger.info("使用NSGA-II多目标优化算法优化排班方案")
                schedule_results = self.nsga2_optimize(schedule_results, all_caregivers)
            elif algorithm_type == 'constraint' and 'ADVANCED_ALGORITHMS_AVAILABLE' in globals() and ADVANCED_ALGORITHMS_AVAILABLE:
                # 使用约束满足算法
                self.logger.info("使用约束满足算法优化排班方案")
                schedule_results = self.constraint_optimize(schedule_results, all_caregivers)
            elif algorithm_type == 'rl' and 'ADVANCED_ALGORITHMS_AVAILABLE' in globals() and ADVANCED_ALGORITHMS_AVAILABLE:
                # 使用强化学习算法
                self.logger.info("使用强化学习算法优化排班方案")
                schedule_results = self.rl_optimize(schedule_results, all_caregivers)
            else:  # hybrid
                # 使用混合算法
                self.logger.info("使用混合算法优化排班方案")
                schedule_results = self.optimize_schedule(schedule_results, all_caregivers)

            end_time = datetime.now()
            optimization_time = (end_time - start_time).total_seconds()
            self.logger.info(f"算法优化完成, 耗时 {optimization_time:.2f} 秒")
        else:
            self.logger.info("算法优化已禁用，跳过优化步骤")

        # 保存到数据库
        self.logger.info(f"开始保存排班计划到数据库...")
        start_time = datetime.now()

        # 根据是否生成照护时间线决定保存的数据
        if not generate_care_timeline:
            # 如果不生成照护时间线，只保存班次分配
            success = self.save_shift_assignments(schedule_results['shift_assignments'])
            self.logger.info(f"仅保存班次分配，不生成照护时间线")
        else:
            # 如果生成照护时间线，保存所有数据
            success = self.save_schedule(schedule_results, start_date)
            self.logger.info(f"保存所有数据，包括照护时间线")

        end_time = datetime.now()
        save_time = (end_time - start_time).total_seconds()

        if success:
            self.logger.info(f"排班计划保存成功, 耗时 {save_time:.2f} 秒")
        else:
            self.logger.error(f"排班计划保存失败, 耗时 {save_time:.2f} 秒")

        # 返回结果摘要
        result = {
            'success': success,
            'schedule_type': schedule_type,
            'start_date': start_date.strftime("%Y-%m-%d") if isinstance(start_date, date) else start_date,
            'shift_assignments_count': len(schedule_results['shift_assignments']),
            'generate_care_timeline': generate_care_timeline,
            'shift_mode': self.active_mode,
            'shift_mode_name': self.shift_mode_config[self.active_mode]['name'],
            'enabled_shifts': self.shift_control['enabled_shifts'],
            'time_slots_per_day': self.time_slots_per_day,
            'time_interval_minutes': self.time_interval_minutes
        }

        # 如果生成了照护时间线，添加相关统计信息
        if generate_care_timeline:
            # 计算排班结果数量
            # 注意：每个老人每天应该有一条排班结果记录
            schedule_results_count = 0
            for _, elder_schedule in schedule_results.get('elder_schedules', {}).items():
                # 如果是单个对象
                if 'schedule_result' in elder_schedule:
                    schedule_results_count += 1
                # 如果是列表
                elif 'schedule_results' in elder_schedule:
                    schedule_results_count += len(elder_schedule['schedule_results'])

            # 计算时间段数量
            time_slots_count = 0
            for elder_schedule in schedule_results.get('elder_schedules', {}).values():
                if 'time_slots' in elder_schedule:
                    time_slots_count += len(elder_schedule.get('time_slots', []))

            result.update({
                'schedule_results_count': schedule_results_count,
                'time_slots_count': time_slots_count
            })

        self.logger.info(f"排班结果摘要: {result}")
        return result

    def nsga2_optimize(self, schedule, caregivers):
        """使用NSGA-II多目标优化算法优化排班方案

        Args:
            schedule: 初始排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        if not hasattr(self, 'nsga2_optimizer'):
            self.logger.warning("NSGA-II优化器未初始化，使用混合算法代替")
            return self.optimize_schedule(schedule, caregivers)

        self.logger.info("开始使用NSGA-II多目标优化算法优化排班方案...")

        # 定义目标函数
        def objective_coverage(schedule):
            return self.evaluate_coverage(schedule)

        def objective_workload_balance(schedule):
            return self.evaluate_workload_balance(schedule, caregivers)

        def objective_preference_satisfaction(schedule):
            return self.evaluate_preference_satisfaction(schedule, caregivers)

        def objective_continuity(schedule):
            return self.evaluate_continuity(schedule)

        # 定义目标函数列表
        objective_functions = [
            objective_coverage,
            objective_workload_balance,
            objective_preference_satisfaction,
            objective_continuity
        ]

        # 定义创建变异解决方案的函数
        def create_variant_func(solution, _):
            return self.create_schedule_variant(solution, caregivers)

        # 定义交叉函数
        def crossover_func(parent1, parent2):
            return self.crossover(parent1, parent2)

        # 定义变异函数
        def mutate_func(solution, _):
            return self.mutate(solution, caregivers)

        # 使用NSGA-II算法优化
        optimized_schedule = self.nsga2_optimizer.optimize(
            initial_solution=schedule,
            objective_functions=objective_functions,
            create_variant_func=create_variant_func,
            crossover_func=crossover_func,
            mutate_func=mutate_func,
            context=None
        )

        # 评估最终适应度
        final_fitness = self.evaluate_schedule(optimized_schedule, caregivers)
        self.logger.info(f"NSGA-II优化完成，最终适应度: {final_fitness}")

        return optimized_schedule

    def constraint_optimize(self, schedule, caregivers):
        """使用约束满足算法优化排班方案

        Args:
            schedule: 初始排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        if not hasattr(self, 'constraint_solver'):
            self.logger.warning("约束满足求解器未初始化，使用混合算法代替")
            return self.optimize_schedule(schedule, caregivers)

        self.logger.info("开始使用约束满足算法优化排班方案...")

        # 获取老人列表
        elders = self.get_elders()

        # 获取班次配置
        shift_config = self.current_shift_times

        # 使用约束满足算法优化
        optimized_schedule = self.constraint_solver.solve(
            initial_schedule=schedule,
            caregivers=caregivers,
            elders=elders,
            shift_config=shift_config,
            max_iterations=1000
        )

        # 评估最终适应度
        final_fitness = self.evaluate_schedule(optimized_schedule, caregivers)
        self.logger.info(f"约束满足算法优化完成，最终适应度: {final_fitness}")

        return optimized_schedule

    def rl_optimize(self, schedule, caregivers):
        """使用强化学习算法优化排班方案

        Args:
            schedule: 初始排班方案
            caregivers: 护理员列表

        Returns:
            优化后的排班方案
        """
        if not hasattr(self, 'rl_scheduler'):
            self.logger.warning("强化学习调度器未初始化，使用混合算法代替")
            return self.optimize_schedule(schedule, caregivers)

        self.logger.info("开始使用强化学习算法优化排班方案...")

        # 获取历史排班数据
        historical_schedules = self.get_historical_schedules()

        # 获取老人列表
        elders = self.get_elders()

        # 获取班次配置
        shift_config = self.current_shift_times

        # 训练强化学习模型
        if historical_schedules:
            self.logger.info(f"使用 {len(historical_schedules)} 条历史排班数据训练强化学习模型")
            self.rl_scheduler.train(
                historical_schedules=historical_schedules,
                caregivers=caregivers,
                elders=elders,
                shift_config=shift_config,
                episodes=500
            )
        else:
            self.logger.warning("没有历史排班数据，跳过训练阶段")

        # 将排班方案转换为状态
        initial_state = self.rl_scheduler.schedule_to_state(schedule, caregivers, elders, shift_config)

        # 使用强化学习生成排班方案
        optimized_schedule = self.rl_scheduler.generate_schedule(
            initial_state=initial_state,
            caregivers=caregivers,
            elders=elders,
            shift_config=shift_config,
            max_steps=100
        )

        # 评估最终适应度
        final_fitness = self.evaluate_schedule(optimized_schedule, caregivers)
        self.logger.info(f"强化学习算法优化完成，最终适应度: {final_fitness}")

        return optimized_schedule

    def get_historical_schedules(self, limit=50):
        """获取历史排班数据

        Args:
            limit: 最大获取数量

        Returns:
            历史排班数据列表
        """
        # 从数据库中获取历史排班数据
        try:
            # 获取最近的排班结果
            schedule_results = ScheduleResult.query.order_by(ScheduleResult.schedule_date.desc()).limit(limit).all()

            # 获取对应的班次分配
            historical_schedules = []
            for result in schedule_results:
                # 获取该日期的班次分配
                shift_assignments = ShiftAssignment.query.filter_by(shift_date=result.schedule_date).all()

                # 获取该日期的时间段分配
                time_slots = TimeSlot.query.filter_by(schedule_date=result.schedule_date).all()

                # 构建排班方案
                schedule = {
                    'shift_assignments': shift_assignments,
                    'schedule_results': [result],
                    'time_slots': time_slots,
                    'elder_schedules': {}
                }

                historical_schedules.append(schedule)

            self.logger.info(f"从数据库中获取到 {len(historical_schedules)} 条历史排班数据")
            return historical_schedules
        except Exception as e:
            self.logger.error(f"获取历史排班数据失败: {str(e)}")
            return []

    def evaluate_coverage(self, schedule):
        """评估排班方案的班次覆盖率

        Args:
            schedule: 排班方案

        Returns:
            班次覆盖率评分，越高越好
        """
        # 获取班次分配
        shift_assignments = schedule.get('shift_assignments', [])
        if not shift_assignments:
            return 0

        # 按日期和班次类型分组
        shifts_by_date_type = {}
        for assignment in shift_assignments:
            date_key = str(assignment.shift_date)
            if date_key not in shifts_by_date_type:
                shifts_by_date_type[date_key] = {}

            shift_type = assignment.shift_type
            if shift_type not in shifts_by_date_type[date_key]:
                shifts_by_date_type[date_key][shift_type] = 0

            shifts_by_date_type[date_key][shift_type] += 1

        # 计算班次覆盖率
        total_coverage = 0
        total_days = len(shifts_by_date_type)
        total_shift_types = len(self.current_shift_times)

        if total_days > 0 and total_shift_types > 0:
            for date_key, shifts in shifts_by_date_type.items():
                coverage = len(shifts) / total_shift_types
                total_coverage += coverage

            average_coverage = total_coverage / total_days
            return average_coverage * 100  # 满分100

        return 0

    def evaluate_workload_balance(self, schedule, caregivers):
        """评估排班方案的工作负载平衡性

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            工作负载平衡性评分，越高越好
        """
        # 获取班次分配
        shift_assignments = schedule.get('shift_assignments', [])
        if not shift_assignments or not caregivers:
            return 0

        # 统计每个护理员的班次数
        caregiver_shifts = {}
        for assignment in shift_assignments:
            caregiver_id = assignment.caregiver_id
            if caregiver_id not in caregiver_shifts:
                caregiver_shifts[caregiver_id] = 0

            caregiver_shifts[caregiver_id] += 1

        # 计算标准差
        if not caregiver_shifts:
            return 0

        shift_counts = list(caregiver_shifts.values())
        mean_shifts = sum(shift_counts) / len(shift_counts)

        if mean_shifts <= 0:
            return 0

        std_dev = (sum((count - mean_shifts) ** 2 for count in shift_counts) / len(shift_counts)) ** 0.5

        # 标准差越小，平衡度越高，评分越高
        balance_score = 50 / (1 + std_dev)  # 最高分值50
        return balance_score

    def evaluate_preference_satisfaction(self, schedule, caregivers):
        """评估排班方案的护理员偏好满足度

        Args:
            schedule: 排班方案
            caregivers: 护理员列表

        Returns:
            护理员偏好满足度评分，越高越好
        """
        # 获取班次分配
        shift_assignments = schedule.get('shift_assignments', [])
        if not shift_assignments or not caregivers:
            return 0

        # 假设每个护理员有偏好的班次类型
        caregiver_preferences = {}
        for caregiver in caregivers:
            # 随机生成偏好，实际应该从数据库获取
            preferred_shifts = random.sample(list(self.current_shift_times.keys()), min(2, len(self.current_shift_times)))
            caregiver_preferences[caregiver.id] = preferred_shifts

        # 计算偏好满足度
        total_assignments = len(shift_assignments)
        satisfied_preferences = 0

        for assignment in shift_assignments:
            caregiver_id = assignment.caregiver_id
            shift_type = assignment.shift_type

            if caregiver_id in caregiver_preferences and shift_type in caregiver_preferences[caregiver_id]:
                satisfied_preferences += 1

        if total_assignments > 0:
            satisfaction_rate = satisfied_preferences / total_assignments
            return satisfaction_rate * 30  # 最高分值30

        return 0

    def evaluate_continuity(self, schedule):
        """评估排班方案的班次连续性

        Args:
            schedule: 排班方案

        Returns:
            班次连续性评分，越高越好
        """
        # 获取班次分配
        shift_assignments = schedule.get('shift_assignments', [])
        if not shift_assignments:
            return 0

        # 按护理员和日期分组
        shifts_by_caregiver = {}
        for assignment in shift_assignments:
            caregiver_id = assignment.caregiver_id
            if caregiver_id not in shifts_by_caregiver:
                shifts_by_caregiver[caregiver_id] = {}

            date_key = str(assignment.shift_date)
            if date_key not in shifts_by_caregiver[caregiver_id]:
                shifts_by_caregiver[caregiver_id][date_key] = []

            shifts_by_caregiver[caregiver_id][date_key].append(assignment.shift_type)

        # 计算连续性分数
        continuity_score = 0
        total_caregivers = len(shifts_by_caregiver)

        for caregiver_id, dates in shifts_by_caregiver.items():
            # 按日期排序
            sorted_dates = sorted(dates.keys())

            # 计算连续工作天数
            consecutive_days = 1
            max_consecutive = 1

            for i in range(1, len(sorted_dates)):
                prev_date = datetime.strptime(sorted_dates[i-1], '%Y-%m-%d').date()
                curr_date = datetime.strptime(sorted_dates[i], '%Y-%m-%d').date()

                if (curr_date - prev_date).days == 1:
                    consecutive_days += 1
                else:
                    consecutive_days = 1

                max_consecutive = max(max_consecutive, consecutive_days)

            # 连续工作3-5天为最佳
            if 3 <= max_consecutive <= 5:
                continuity_score += 1

        if total_caregivers > 0:
            return (continuity_score / total_caregivers) * 20  # 最高分值20

        return 0
